package com.yanqu.road.server.manager.activity.chuhan.activity;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yanqu.road.dao.impl.player.CrossCommonActivityDataDaoImpl;
import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.autodata.CrossChuHanRankSort;
import com.yanqu.road.entity.autodata.chuhan.config.*;
import com.yanqu.road.entity.autodata.chuhan.data.*;
import com.yanqu.road.entity.autodata.chuhan.join.CrossChuHanUnionJoin;
import com.yanqu.road.entity.autodata.chuhan.join.CrossChuHanUnionJoinMember;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.config.patrons.PatronsInfo;
import com.yanqu.road.entity.enums.*;
import com.yanqu.road.entity.log.cross.chuhan.*;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.entity.scene.ScenePlayerInfo;
import com.yanqu.road.entity.scene.UserScene;
import com.yanqu.road.entity.union.UnionBaseInfo;
import com.yanqu.road.entity.union.activitygroup.UnionActivityGroupUnion;
import com.yanqu.road.logic.activity.chuhan.ChuHanMapGraph;
import com.yanqu.road.logic.activity.chuhan.ChuHanMapLogic;
import com.yanqu.road.logic.activity.chuhan.ChuHanMapNode;
import com.yanqu.road.logic.config.GameConfig;
import com.yanqu.road.logic.helper.ActivityHelper;
import com.yanqu.road.logic.helper.UnionActivityGroupHelper;
import com.yanqu.road.logic.pb.CrossChuHanPb;
import com.yanqu.road.logic.pb.PlayerBasePb;
import com.yanqu.road.logic.pb.UnionBasePb;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.pb.activity.ChuHanProto;
import com.yanqu.road.pb.commonactivity.CommonActivityProto;
import com.yanqu.road.pb.cross.activity.CrossActivityProto;
import com.yanqu.road.pb.player.PlayerProto;
import com.yanqu.road.pb.rank.RankProto;
import com.yanqu.road.pb.worldmessage.WorldMessageProto;
import com.yanqu.road.server.logic.MessageHelper;
import com.yanqu.road.server.manager.activity.ActivityMgr;
import com.yanqu.road.server.manager.activity.chuhan.logic.CrossChuHanChatLogic;
import com.yanqu.road.server.manager.activity.chuhan.task.BaseCrossChuHanTask;
import com.yanqu.road.server.manager.activity.chuhan.task.impl.*;
import com.yanqu.road.server.manager.config.GoodsMgr;
import com.yanqu.road.server.manager.config.PatronsMgr;
import com.yanqu.road.server.manager.log.CrossAutoLogMgr;
import com.yanqu.road.server.manager.player.WorldMessageMgr;
import com.yanqu.road.server.manager.scene.CrossSceneMgr;
import com.yanqu.road.server.manager.union.activitygroup.Cross2UnionActivityGroupMgr;
import com.yanqu.road.server.manager.user.CrossUserMgr;
import com.yanqu.road.server.protocol.*;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.mina.util.ConcurrentHashSet;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class CrossChuHanActivity extends CrossChuHanBaseActivity {

    private final RandomHelper randomHelper = new RandomHelper();

    private final int groupId;

    private AtomicInteger reportId = new AtomicInteger(1);
    /**
     * 活动总数据
     */
    private CrossChuHanData crossChuHanData;

    /**
     * 商会数据
     */
    private Map<String, CrossChuHanUnionJoin> unionJoinMap = new ConcurrentHashMap<>();

    /**
     * 玩家数据
     */
    private Map<Long, CrossChuHanUserData> userDataMap = new ConcurrentHashMap<>();

    /**
     * 阵营数据
     */
    private Map<Integer, CrossChuHanCampData> campDataMap = new ConcurrentHashMap<>();

    /**
     * 城池战报
     */
    private List<CrossChuHanCityWarReport> cityWarReportList = new ArrayList();

    /**
     * 个人战报
     */
    private List<CrossChuHanUserWarReport> userWarReportsList = new ArrayList<>();

    // ============================== 缓存数据 ==============================

    // 待入库战报
    private List<CrossChuHanUserWarReport> needInsertUserReportList = new ArrayList();
    private List<CrossChuHanCityWarReport> needInsertCityReportList = new ArrayList();

    // 聊天
    private CrossChuHanChatLogic chatLogic;

    // 榜单
    private CrossChuHanRankListModel userRank = new CrossChuHanRankListModel(); // 个人榜
    private Map<Integer, CrossChuHanRankListModel> campUserRankMap = new ConcurrentHashMap(); // 阵营内个人榜
    private Map<Integer, CrossChuHanRankListModel> campUnionRankMap = new ConcurrentHashMap<>(); // 阵营内商会榜

    /**
     * PVE个人榜,key:cityType
     */
    private Map<Integer, CrossChuHanRankListModel> pveUserRankMap = new ConcurrentHashMap(); // PVE总个人榜

    // 城主职位
    private Map<Long, List<CrossChuHanUserData>> needSyncPosUserData = new ConcurrentHashMap<>();

    // 需要同步的队伍
    private final Set<String> needSyncTeam = new ConcurrentHashSet<>();
    // 需要同步兵营的玩家
    private final Set<Long> needSyncBarracksUsers = new ConcurrentHashSet<>();
    // 需要同步兵营的玩家
    private final Set<Long> needSyncDataUsers = new ConcurrentHashSet<>();
    // 需要同步的城池
    private final Set<Integer> needSyncCity = new ConcurrentHashSet<>();

    /**
     * PVE第一的玩家,KEY:cityType
     */
    private Map<Integer, CrossChuHanUserData> pveFirstUserDataMap = new ConcurrentHashMap<>();
    /**
     * PVE阵营内第一的玩家,KEY:cityType,KEY:campId
     */
    private Map<Integer, Map<Integer, CrossChuHanUserData>> pveCampFirstUserDataMap = new ConcurrentHashMap<>();

    private AtomicBoolean isNeedSyncBuff = new AtomicBoolean(false);
    private AtomicBoolean isNeedSyncOrderCityTask = new AtomicBoolean(false);

    // ============================== 构造方法 ==============================

    public CrossChuHanActivity(ActivityInfo activityInfo, int groupId) {
        super(activityInfo);
        this.groupId = groupId;

        // 【读取数据】
        loadDb();
        // 【其他初始化】
        //初始化阵营BUFF
        for (CrossChuHanCampData campData : campDataMap.values()) {
            refreshCampBuff(campData.getCampId());
        }
        initUserRedPacketNumMap(); // 初始化红包发送数量
        initNeedSyncPositionUser();
        chatLogic = new CrossChuHanChatLogic(activityInfo, groupId);
        initAllRankList();

        // 启动线程
        initTask();
        thread.start();
    }

    public void loadDb() {
        ActivityInfo activityInfo = getConfig().getActivityInfo();
        crossChuHanData = new CrossCommonActivityDataDaoImpl<CrossChuHanData>().getActivityData(CrossChuHanData.class, activityInfo.getActivityId(), groupId);
        if (crossChuHanData == null) {
            crossChuHanData = new CrossChuHanData();
            crossChuHanData.setActivityId(activityInfo.getActivityId());
            crossChuHanData.setGroupId(groupId);
            crossChuHanData.setLockUnion(false);
            crossChuHanData.setProcess2WinUserId(0L);
            crossChuHanData.setRefreshCityEventTime(config.getStage1TimeList().get(0).get(0)); // 初始化为一阶段开始时间
            crossChuHanData.setApplyCityEventTime(config.getStage1TimeList().get(0).get(0)); // 初始化为一阶段开始时间
            crossChuHanData.setNpcEventAddTime(config.getStage1TimeList().get(0).get(0) - config.getNpcEventInterval()); // 这样一阶段开始时才能生成一个
            crossChuHanData.setCityMap(createCity());
            crossChuHanData.setInsertOption();
        }

        Map<Integer, CrossChuHanCityData> cityMap = new CrossCommonActivityDataDaoImpl<CrossChuHanCityData>().getActivityDataIntMap(CrossChuHanCityData.class, activityInfo.getActivityId(), groupId, "cityId");
        if (cityMap.isEmpty()) {
            cityMap = createCity();
        }
        crossChuHanData.setCityMap(cityMap);

        unionJoinMap = new CrossCommonActivityDataDaoImpl<CrossChuHanUnionJoin>().getActivityDataStringMap(CrossChuHanUnionJoin.class, activityInfo.getActivityId(), groupId, "unionUid");

        userDataMap = new CrossCommonActivityDataDaoImpl<CrossChuHanUserData>().getActivityDataLongMap(CrossChuHanUserData.class, activityInfo.getActivityId(), groupId, "userId");

        cityWarReportList = new CrossCommonActivityDataDaoImpl<CrossChuHanCityWarReport>().getActivityDataList(CrossChuHanCityWarReport.class, activityInfo.getActivityId(), groupId);
        cityWarReportList.sort(Comparator.comparingLong(CrossChuHanCityWarReport::getTime));

        userWarReportsList = new CrossCommonActivityDataDaoImpl<CrossChuHanUserWarReport>().getActivityDataList(CrossChuHanUserWarReport.class, activityInfo.getActivityId(), groupId);
        userWarReportsList.sort(Comparator.comparingLong(CrossChuHanUserWarReport::getTime));

        //更新一下战报id
        for (CrossChuHanUserWarReport report : userWarReportsList) {
            if (report.getNo() >= reportId.get()) {
                reportId.set(report.getNo() + 1);
            }
        }

        campDataMap = new CrossCommonActivityDataDaoImpl<CrossChuHanCampData>().getActivityDataIntMap(CrossChuHanCampData.class, activityInfo.getActivityId(), groupId, "campId");


    }

    // ============================== 数据生成获取 ==============================

    private CrossChuHanUserData initUserData(long userId, String unionUid) {
        // 生成userData
        if (!userDataMap.containsKey(userId)) {
            synchronized (lock) {
                if (!userDataMap.containsKey(userId)) {
                    CrossChuHanUserData userData = new CrossChuHanUserData(config.getActivityInfo().getActivityId(), groupId, userId);
                    userData.setUnionUid(unionUid);

                    // 初始化兵营部分数据
                    CrossChuHanBarracks barracks = userData.getBarracks();
                    barracks.setHospitalSize(config.getWoundedSoldierMaxNum());
                    barracks.setRescueSpeed(config.getWoundedRescueTime());
                    barracks.setRescueCount(config.getWoundedRescueCount());
                    userData.setUpdateOption();

                    // 加到map
                    userDataMap.put(userId, userData);
                }
            }
        }
        return userDataMap.get(userId);
    }

    /**
     * 初始化需要同步的会获得银两的职位的玩家
     */
    private void initNeedSyncPositionUser() {
        Map<Integer, ChuHanTitleConfig> titleConfigMap = config.getTitleConfigMap();
        for (CrossChuHanUserData userData : userDataMap.values()) {
            if (userData.getPositionId() != eTongBingPosition.None.getValue()) {
                if (titleConfigMap.get(userData.getPositionId()).getType() == 1) {
                    addNeedSyncPosUser(userData);
                }
            }
        }
    }

    /**
     * 初始化阵营数据
     */
    private void initCampUserData() {

        logger.info("initCampUserData activityId {}. groupId:{}", config.getActivityInfo().getActivityId(), groupId);

        // 阵营数据生成
        for (int campId = 1; campId <= config.getCampNum(); campId++) {
            if (!campDataMap.containsKey(campId)) {
                CrossChuHanCampData campData = new CrossChuHanCampData();
                campData.setActivityId(config.getActivityInfo().getActivityId());
                campData.setGroupId(groupId);
                campData.setCampId(campId);
                campData.setInsertOption();
                campDataMap.put(campId, campData);
            } else {
                // 已有数据说明有问题...
                logger.error("initCampUserData, repeated create data? activityId {}, campId {}.groupId:{}", config.getActivityInfo().getActivityId(), campId, groupId);
                CrossChuHanCampData campData = campDataMap.get(campId);
                campData.setRedPacketMap(new ConcurrentHashMap<>());
                campData.setRedPacketValue(0L);
                campData.setUserRedPacketMap(new ConcurrentHashMap<>());
                campDataMap.put(campId, campData);
            }

            refreshCampBuff(campId);
        }
    }

    //触发玩家被任命后的增益银两收成，这里只用定时器去触发，就不加锁了
    //需求是城主加成英两且获得别人扣除的英两，通过领取，增益buff的也是加银两然后领取，减益buff的玩家要去本服他的英两收益变小
    public void makePosSliver() {
        //领奖期才能任命
        long nowTime = System.currentTimeMillis();
        if (nowTime < config.getActivityInfo().getEndTime() * 1000 || nowTime > config.getActivityInfo().getEndShowTime() * 1000) {
            return;
        }
        Map<Integer, ChuHanTitleConfig> titleConfigMap = config.getTitleConfigMap();
        for (CrossChuHanUserData userData : userDataMap.values()) {
            synchronized (lock) {
                if (userData.getPositionId() != eTongBingPosition.None.getValue()) {
                    ChuHanTitleConfig titleConfig = titleConfigMap.get(userData.getPositionId());
                    if (titleConfig.getType() == 1) {
                        /*long lastCalTime = userData.getPosAddSilverTime();
                        long sec = (nowTime - lastCalTime) / 1000;
                        Map<Long, BigInteger> addMap = userData.getPosAddSilverMap();
                        BigInteger earnSpeed = getUserEarnSpeed(userData.getUserId());
                        BigInteger oldValue = addMap.getOrDefault(userData.getUserId(), BigInteger.ZERO);
                        BigInteger addValue = BigInteger.valueOf(sec).multiply(earnSpeed).multiply(BigInteger.valueOf(titleConfig.getEffect())).divide(BigInteger.valueOf(1000));
                        addMap.put(userData.getUserId(), oldValue.add(addValue));
                        userData.setPosAddSilverMap(addMap);
                        userData.setPosAddSilverTime(nowTime);*/
                    } else if (titleConfig.getType() == 2) {
                        long lastCalTime = userData.getPosAddSilverTime();
                        long sec = (nowTime - lastCalTime) / 1000;
                        BigInteger earnSpeed = getUserEarnSpeed(userData.getUserId());
                        BigInteger addValue = BigInteger.valueOf(sec).multiply(earnSpeed).multiply(BigInteger.valueOf(titleConfig.getEffect())).divide(BigInteger.valueOf(1000));
                        // 累计上缴银两
                        userData.setTotalPosSilver(userData.getTotalPosSilver().add(addValue));
                        //每个人分配到的不一样
                        posSliverAdd(addValue,userData.getUserId());
                        userData.setPosAddSilverTime(nowTime);
                    }
                }
            }
        }
        syncUserPositionSliver();
    }

    //按照配在百分百算钱
    private void posSliverAdd(BigInteger addValue,long userId) {
        Map<Integer, ChuHanTitleConfig> titleConfigMap = config.getTitleConfigMap();
        int allAddWeight = 0;
        for (ChuHanTitleConfig titleConfig : titleConfigMap.values()) {
            if (titleConfig.getType() == 1) {
                allAddWeight = allAddWeight + titleConfig.getEffect();
            }
        }
        for (CrossChuHanUserData userData : userDataMap.values()) {
            ChuHanTitleConfig titleConfig = titleConfigMap.get(userData.getPositionId());
            if (titleConfig != null && titleConfig.getType() == 1) {
                Map<Long, BigInteger> addMap = userData.getPosAddSilverMap();
                BigInteger oldValue = addMap.getOrDefault(userId, BigInteger.ZERO);
                BigInteger userAdd = addValue.multiply(BigInteger.valueOf(titleConfig.getEffect())).divide(BigInteger.valueOf(allAddWeight));
                // 累计收取银两
                userData.setTotalPosSilver(userData.getTotalPosSilver().add(userAdd));
                addMap.put(userId, oldValue.add(userAdd));
                userData.setUpdateOption();
            }
        }
    }

    //添加任命完的人的
    private void addNeedSyncPosUser(CrossChuHanUserData userData) {
        synchronized (lock) {
            UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userData.getUserId());
            if (userBaseInfo != null) {
                if (!needSyncPosUserData.containsKey(userBaseInfo.getServerId())) {
                    needSyncPosUserData.put(userBaseInfo.getServerId(), new ArrayList<>());
                }
                needSyncPosUserData.get(userBaseInfo.getServerId()).add(userData);
            }
        }
    }

    private Map<Long, List<CrossChuHanUserData>> getNeedSyncPosUserDataMap() {
        synchronized (lock) {
            if (needSyncPosUserData.size() == 0) {
                Map<Long, List<CrossChuHanUserData>> tempMap = new ConcurrentHashMap<>();
                Map<Integer, ChuHanTitleConfig> titleConfigMap = config.getTitleConfigMap();
                for (CrossChuHanUserData userData : userDataMap.values()) {
                    if (userData.getPositionId() != eTongBingPosition.None.getValue()) {
                        ChuHanTitleConfig titleConfig = titleConfigMap.get(userData.getPositionId());
                        if (titleConfig.getType() == 1) {
                            UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userData.getUserId());
                            if (userBaseInfo != null) {
                                if (!tempMap.containsKey(userBaseInfo.getServerId())) {
                                    tempMap.put(userBaseInfo.getServerId(), new ArrayList<>());
                                }
                                tempMap.get(userBaseInfo.getServerId()).add(userData);
                            }
                        }
                    }
                }
                needSyncPosUserData = tempMap;
            }
            return needSyncPosUserData;
        }
    }

    //同步玩家身上的数据
    public void syncUserData(long userId, ChuHanProto.ChuHanUserDataSyncMsg reqMsg) {
        ChuHanProto.ChuHanUserDataSyncMsg.Builder respMsg = reqMsg.toBuilder();
        respMsg.setWinnerId(crossChuHanData.getProcess2WinUserId());
        CrossChuHanUserData userData = getUserData(userId);
        if (userData != null) {
            respMsg.setUnionScore(getUnionZhanGong(userData.getUnionUid()));
        }
        sendPacket(Protocol.S_CHUHAN_USER_DATA_SYNC % 20000, respMsg, userId);
    }

    //城池有了同步客户端告诉他要出图标了
    public void syncWinnerGetNotice() {
        List<Long> serverList;
        if (UnionActivityGroupHelper.needGroup(config.getActivityInfo())) {
            serverList = new ArrayList<>(Cross2UnionActivityGroupMgr.getGroupData(config.getActivityInfo().getActivityId(), groupId).getServerSet());
        } else {
            serverList = config.getActivityInfo().getServerIdList();
        }

        // 重复发也不要紧
        for (long serverId : serverList) {
            ChuHanProto.CrossChuHanReqMsg.Builder reqMsg = ChuHanProto.CrossChuHanReqMsg.newBuilder();
            reqMsg.setActivityId(config.getActivityInfo().getActivityId());
            MessageHelper.sendPacket(serverId, 0, YanQuMessageUtils.buildMessage(GameProtocol.S_CHUHAN_SYNC_USER_DATA, reqMsg));
        }
    }

    //同步有职位的玩家的领取奖励数信息
    public void syncUserPositionSliver() {
        Map<Long, List<CrossChuHanUserData>> needSyncUserDataMap = new ConcurrentHashMap<>(getNeedSyncPosUserDataMap());
        for (Long serverId : needSyncUserDataMap.keySet()) {
            ChuHanProto.CrossChuHanPositionSliverSyncMsg.Builder syncMsg = ChuHanProto.CrossChuHanPositionSliverSyncMsg.newBuilder();
            List<CrossChuHanUserData> userDataList = needSyncPosUserData.get(serverId);
            for (CrossChuHanUserData userData : userDataList) {
                ChuHanProto.CrossChuHanPositionUserMsg.Builder userMsg = ChuHanProto.CrossChuHanPositionUserMsg.newBuilder();
                userMsg.setUserId(userData.getUserId());
                Map<Long, BigInteger> addMap = userData.getPosAddSilverMap();
                for (long userId : addMap.keySet()) {
                    UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
                    if (userBaseInfo != null) {
                        ChuHanProto.ChuHanPositionSliverMsg.Builder sliverUserMsg = ChuHanProto.ChuHanPositionSliverMsg.newBuilder();
                        sliverUserMsg.setUserId(userId);
                        sliverUserMsg.setSliver(addMap.get(userId).toString());
                        sliverUserMsg.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
                        userMsg.addSliverUser(sliverUserMsg);
                    }
                }
                syncMsg.addUser(userMsg);
            }
            MessageHelper.sendPacket(serverId, 0, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_POS_REWARD_SYNC, syncMsg));
        }
    }

    //领取职位产生的银两奖励
    public void getPosReward(long serverId, long userId) {
        ChuHanProto.ChuHanPosGetRewardRespMsg.Builder respMsg = ChuHanProto.ChuHanPosGetRewardRespMsg.newBuilder();
        respMsg.setRet(0);
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_GET_POS_REWARD % 20000, respMsg, userId);
            return;
        }
        synchronized (lock) {
            if (userData.getPositionId() == eTongBingPosition.None.getValue()) {
                respMsg.setRet(GameErrorCode.E_CHUHAN_POS_REWARD_CAN_NOT_GET);
                sendPacket(Protocol.S_CHUHAN_GET_POS_REWARD % 20000, respMsg, userId);
                return;
            }
            Map<Integer, ChuHanTitleConfig> titleConfigMap = config.getTitleConfigMap();
            ChuHanTitleConfig titleConfig = titleConfigMap.get(userData.getPositionId());
            if (titleConfig.getType() != 1) {
                respMsg.setRet(GameErrorCode.E_CHUHAN_POS_REWARD_CAN_NOT_GET);
                sendPacket(Protocol.S_CHUHAN_GET_POS_REWARD % 20000, respMsg, userId);
                return;
            }
            Map<Long, BigInteger> rewardMap = userData.getPosAddSilverMap();
            BigInteger allValue = BigInteger.ZERO;
            for (long unLuckUserId : rewardMap.keySet()) {
                BigInteger value = rewardMap.get(unLuckUserId);
                allValue = allValue.add(value);
                rewardMap.put(unLuckUserId, BigInteger.ZERO);
                userData.setUpdateOption();
            }
            if (allValue.compareTo(BigInteger.ZERO) == 0) {
                respMsg.setRet(GameErrorCode.E_CHUHAN_NO_POS_REWARD);
                sendPacket(Protocol.S_CHUHAN_GET_POS_REWARD % 20000, respMsg, userId);
                return;
            }
            userData.setPosAddSilverMap(rewardMap);
            respMsg.setReward(GameConfig.GAME_MONEY_SILVER + "=" + allValue.toString());
        }
        MessageHelper.sendPacket(serverId, userId, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_GET_POS_REWARD, respMsg));
    }

    /**
     * 触发城池的采集收益 只触发对应阵营的
     * 玩家点击要领取的时候触发下，被打下来触发下，buff失效的时候触发下
     */
    private void makeAllCityCollect(int campId, long dealTime) {
        // 阵营空、未生成数据，不处理
        if (campId == 0 || !campDataMap.containsKey(campId)) {
            return;
        }

        List<Integer> needDealCity = new ArrayList<>();
        long nowTime = dealTime;
        Map<Integer, ChuHanCityConfig> cityConfigMap = config.getCityConfigMap();
        Map<Integer, CrossChuHanCityData> cityMap = crossChuHanData.getCityMap();
        CrossChuHanCampData campData = campDataMap.get(campId);

        synchronized (lock) {
            // 获取有收益的城池
            for (CrossChuHanCityData city : cityMap.values()) {
                if (city.getCampId() != campId) {
                    continue;
                }
                ChuHanCityConfig cityConfig = cityConfigMap.get(city.getCityId());
                ChuHanCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap().get(cityConfig.getType());
                // 每小时产生一次收益
                int bingFuProduct = getCityBingFuProductBuffPer(city.getCityId());
                if ((cityTypeConfig.getFoodProduction() > 0 || bingFuProduct > 0)
                        && nowTime - city.getCollectionTime() >= DateHelper.HOUR_MILLIONS) {
                    needDealCity.add(city.getCityId());
                }
            }
            // 城池加采集奖励
            for (int cityId : needDealCity) {
                // 计算产量
                Property reward = collectCityProduct(cityId, nowTime);
                // 给所有玩家加
                addAllUserCityProductReward(campId, cityId, reward);
            }

            if (!needDealCity.isEmpty()) {//红点
                for (CrossChuHanUserData userData : userDataMap.values()) {
                    List<Long> foods = new ArrayList<>(userData.getFoodMap().values());
                    long totalFood = 0;
                    for (Long food : foods) {
                        totalFood += food;
                    }
                    if (totalFood > config.getRedDotCollectCount()) {
                        addRedDot(userData, eTongBingRedDotType.Type10_GetCollect);
                    }
                }
            }
        }
    }

    /**
     * buff后产量
     */
    private long getCityFoodProductBuffPer(int cityId) {
        Map<Integer, CrossChuHanCityData> cityMap = crossChuHanData.getCityMap();
        CrossChuHanCityData cityData = cityMap.get(cityId);
        ChuHanCityConfig cityConfig = config.getCityConfigMap().get(cityId);
        ChuHanCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap().get(cityConfig.getType());
        Integer buffAddition = getBuffAddition(cityData.getCampId(), eChuHanBuffType.Type5_CityFoodProduce).get(0);
        BigDecimal production = BigDecimal.valueOf(cityTypeConfig.getFoodProduction())
                .multiply(BigDecimal.valueOf(1000 + buffAddition)).divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN);
        return production.longValue();
    }

    private int getCityBingFuProductBuffPer(int cityId) {
        Map<Integer, CrossChuHanCityData> cityMap = crossChuHanData.getCityMap();
        CrossChuHanCityData cityData = cityMap.get(cityId);
        ChuHanCityConfig cityConfig = config.getCityConfigMap().get(cityId);
        if (cityConfig.getBuffId() > 0) {
            int buffId = cityConfig.getBuffId();
            ChuHanBuffTypeConfig buffConfig = getConfig().getBuffTypeConfigMap().get(buffId);
            if (buffConfig != null && buffConfig.getType() == eChuHanBuffType.Type8_BingFuProduct.getValue()) {
                return getBuffAddition(cityData.getCampId(), eChuHanBuffType.Type8_BingFuProduct).get(0);
            }
        }
        ChuHanCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap().get(cityConfig.getType());
        if (cityTypeConfig.getBuffEffect() > 0) {
            int buffId = cityTypeConfig.getBuffEffect();
            ChuHanBuffTypeConfig buffConfig = getConfig().getBuffTypeConfigMap().get(buffId);
            if (buffConfig != null && buffConfig.getType() == eChuHanBuffType.Type8_BingFuProduct.getValue()) {
                return getBuffAddition(cityData.getCampId(), eChuHanBuffType.Type8_BingFuProduct).get(0);
            }
        }
        return 0;
    }

    /**
     * 城池指定时间的采集产量,并更新采集时间
     *
     * @param cityId
     * @param nowTime
     * @return
     */
    private Property collectCityProduct(int cityId, Long nowTime) {
        Map<Integer, CrossChuHanCityData> cityMap = crossChuHanData.getCityMap();
        CrossChuHanCityData cityData = cityMap.get(cityId);
        // 收益多少次
        long times = (nowTime - cityData.getCollectionTime()) / DateHelper.HOUR_MILLIONS;
        cityData.setCollectionTime(cityData.getCollectionTime() + times * DateHelper.HOUR_MILLIONS);
        crossChuHanData.setUpdateOption();

        Property reward = new Property();
        long food = BigInteger.valueOf(times).multiply(BigInteger.valueOf(getCityFoodProductBuffPer(cityId))).longValue();
        if (food > 0) {
            reward.addProperty(getConfig().getZiZhongItem(), food);
        }

        int bingFu = BigInteger.valueOf(times).multiply(BigInteger.valueOf(getCityBingFuProductBuffPer(cityId))).intValue();
        if (bingFu > 0) {
            reward.addProperty(getConfig().getBingFuItem(), bingFu);
        }
        return reward;
    }

    /**
     * 给同阵营所有玩家加采集奖励
     */
    private void addAllUserCityProductReward(int campId, int cityId, Property reward) {
        if (reward.isNothing()) {
            return;
        }
        for (CrossChuHanUserData userData : userDataMap.values()) {
            if (userData.getCampId() != campId) {
                continue;
            }

            long food = reward.getCountByGoodsId(getConfig().getZiZhongItem()).longValue();
            if (food > 0) {
                userData.addFood(cityId, food);
            }
            int bingFu = reward.getCountByGoodsId(getConfig().getBingFuItem()).intValue();
            if (bingFu > 0) {
                userData.addBingFu(cityId, bingFu);
            }

            addRedDot(userData, eTongBingRedDotType.Type10_GetCollect);
        }
    }

    /**
     * 城池采集界面信息
     */
    public void cityFoodInfo(long userId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            // 没资格的不可能打开采集界面
            return;
        }

        int campId = userData.getCampId();

        // 重新计算采集
        makeAllCityCollect(campId, System.currentTimeMillis());

        // 协议
        ChuHanProto.ChuHanCityFoodRespMsg.Builder respMsg = ChuHanProto.ChuHanCityFoodRespMsg.newBuilder();
        respMsg.setRet(0);

        Set<Integer> hadCitySet = new HashSet<>();

        CrossChuHanCampData campData = campDataMap.get(campId);
        if (campData != null) {
            // 有收益的城池
            for (Integer cityId : userData.getHaveCollectCity()) {
                long foodValue = userData.getFoodValue(cityId);
                int bingFuValue = userData.getBingFuValue(cityId);

                if (foodValue == 0 && bingFuValue == 0) {
                    continue;
                }

                ChuHanProto.ChuHanCityFoodMsg.Builder tempMsg = ChuHanProto.ChuHanCityFoodMsg.newBuilder();
                tempMsg.setCityId(cityId);
                tempMsg.setFood(foodValue);
                tempMsg.setFoodProduction(getCityFoodProductBuffPer(cityId));
                if (bingFuValue > 0){
                    tempMsg.setBingFu(bingFuValue);
                }
                tempMsg.setBingFuProduction(getCityBingFuProductBuffPer(cityId));

                respMsg.addFoods(tempMsg);

                hadCitySet.add(cityId);
            }
        }

        // 无收益但占领的城池
        for (CrossChuHanCityData city : getCrossChuHanData().getCityMap().values()) {
            if (hadCitySet.contains(city.getCityId()) || city.getCampId() != campId) {
                continue;
            }
            ChuHanProto.ChuHanCityFoodMsg.Builder tempMsg = ChuHanProto.ChuHanCityFoodMsg.newBuilder();
            tempMsg.setCityId(city.getCityId());
            tempMsg.setFood(0);
            tempMsg.setFoodProduction(getCityFoodProductBuffPer(city.getCityId()));
            tempMsg.setBingFuProduction(getCityBingFuProductBuffPer(city.getCityId()));
            respMsg.addFoods(tempMsg);
        }

        sendPacket(Protocol.S_CHUHAN_CITY_FOOD % 20000, respMsg, userId);
    }

    /**
     * 城池采集一键收获
     */
    public void cityFoodCollect(long userId) {

        ChuHanProto.CrossChuHanCityFoodCollectRespMsg.Builder builder = ChuHanProto.CrossChuHanCityFoodCollectRespMsg.newBuilder();
        builder.setRet(0);

        CrossChuHanUserData userData = getUserData(userId);

        if (!isAble(userId)) {
            builder.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_CITY_FOOD_COLLECT % 20000, builder, userId);
            return; // 无资格不可能进去点一键收获
        }

        // 重新计算收益
        makeAllCityCollect(userData.getCampId(), System.currentTimeMillis());

        // 统计总奖励
        Property reward = new Property();
        CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
        if (campData != null) {
            synchronized (lock) {
                long allFood = userData.getAllFood();
                if (allFood > 0) {
                    reward.addProperty(new Property(config.getZiZhongItem(), allFood));
                }
                int allBingFu = userData.getAllBingFu();
                if (allBingFu > 0) {
                    reward.addProperty(new Property(config.getBingFuItem(), allBingFu));
                }
                //清空
                userData.setFoodMap(new ConcurrentHashMap<>());
                userData.setBingFuMap(new ConcurrentHashMap<>());
            }
        }

        if (reward.isNothing()) {
            builder.setRet(GameErrorCode.E_CHUHAN_CITY_FOOD_COLLECT_NO_VALUE);
            sendPacket(Protocol.S_CHUHAN_CITY_FOOD_COLLECT % 20000, builder, userId);
            return; // 没奖励
        }

        builder.setReward(reward.toHelperString());
        sendPacket(GameProtocol.S_CROSS_CHUHAN_CITY_FOOD_COLLECT, builder, userId);
    }

    private CrossChuHanRedPacket oneRedPacket(int campId, long userId, int goodId) {
        CrossChuHanCampData campData = campDataMap.get(campId);
        Map<Long, CrossChuHanRedPacket> redPacketMap = campData.getRedPacketMap();

        CrossChuHanRedPacket redPacket;
        synchronized (lock) {
            // 生成红包
            redPacket = new CrossChuHanRedPacket();
            redPacket.setUserId(userId);
            redPacket.setGoodId(goodId);
            redPacket.setTime(System.currentTimeMillis());
            redPacket.setRedPacketNo(redPacketMap.size() + 1);
            // 加map
            redPacketMap.put(redPacket.getRedPacketNo(), redPacket);
            campData.setUpdateOption();
            // 加统计数量
            addUserRedPacketCount(redPacket, campData);
        }

        return redPacket;
    }

    /**
     * 使用道具后给阵营加红包
     */
    public void addRedPacket(long userId, ChuHanProto.CrossChuHanRedPacketAddReqMsg reqMsg) {
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            logger.error("tong bing addRedPacket fail, no able! activityId {}, userId {}.", config.getActivityInfo().getActivityId(), userId);
            return;
        }

        int campId = userData.getCampId();
        CrossChuHanCampData campData = campDataMap.get(campId);
        if (campData == null) {
            logger.error("tong bing addRedPacket fail, no camp data! activityId {}, userId {}.", config.getActivityInfo().getActivityId(), userId);
            return;
        }

        List<CrossChuHanRedPacket> redPacketSyncList = new ArrayList<>();
        synchronized (lock) {
            for (int i = 0; i < reqMsg.getNum(); i++) {
                redPacketSyncList.add(oneRedPacket(campId, userId, reqMsg.getGoodId()));
            }
        }

        if (redPacketSyncList.size() > 0) {
            syncRedPacketToCampUser(userId, redPacketSyncList);

            // 红点
            for (CrossChuHanUserData tmpUserData : userDataMap.values()) {
                if (tmpUserData.getCampId() == campId) {
                    addRedDot(tmpUserData, eTongBingRedDotType.Type9_GetBox);
                }
            }
        }
    }

    // 买完宝箱礼包，同步所有阵营玩家有礼包了
    public void syncRedPacketToCampUser(long userId, List<CrossChuHanRedPacket> redPacketSyncList) {
        CrossChuHanUserData rpUserData = getUserData(userId);
        if (rpUserData == null) {
            return;
        }
        UserBaseInfo rpUserBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        if (rpUserBaseInfo == null) {
            return;
        }
        ChuHanProto.CrossChuHanRedPacketSyncMsg.Builder syncMsg = ChuHanProto.CrossChuHanRedPacketSyncMsg.newBuilder();
        for (CrossChuHanRedPacket redPacket : redPacketSyncList) {
            ChuHanProto.ChuHanRedPacketMsg.Builder redPacketMsg = ChuHanProto.ChuHanRedPacketMsg.newBuilder();
            redPacketMsg.setUserId(userId);
            redPacketMsg.setNickName(rpUserBaseInfo.getNickName());
            redPacketMsg.setUnionName(rpUserBaseInfo.getUnionName());
            redPacketMsg.setType(redPacket.getGoodId());
            redPacketMsg.setIsGet(0);
            redPacketMsg.setOrderNo("" + redPacket.getRedPacketNo());
            syncMsg.addRedPackets(redPacketMsg);
        }
        List<Long> serverIdList = new ArrayList<>();
        for (CrossChuHanUserData userData : userDataMap.values()) {
            if (userData.getCampId() == rpUserData.getCampId()) {
                UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userData.getUserId());
                if (userBaseInfo != null) {
                    syncMsg.addUserIds(userData.getUserId());
                    if (!serverIdList.contains(userBaseInfo.getServerId())) {
                        serverIdList.add(userBaseInfo.getServerId());
                    }
                }
            }
        }
        for (long serverId : serverIdList) {
            MessageHelper.sendPacket(serverId, 0, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_RED_PACKET_SYNC, syncMsg));
        }
    }

    //同步宝箱值去本服改condition
    public void syncRedPacketBoxValue() {
        if (!ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return;
        }
        for (CrossChuHanCampData campData : new ConcurrentHashMap<>(campDataMap).values()) {
            if (campData.getRedPacketValue() > 0 && campData.getCampId() != 0) {
                Map<Long, List<Long>> serverUserMap = getCampServerIdUserIdMap(campData.getCampId());
                for (long serverId : serverUserMap.keySet()) {
                    ChuHanProto.CrossChuHanRedPacketBoxValueSyncMsg.Builder syncMsg = ChuHanProto.CrossChuHanRedPacketBoxValueSyncMsg.newBuilder();
                    syncMsg.setActivityId(campData.getActivityId());
                    for (long userId : serverUserMap.get(serverId)) {
                        syncMsg.addUserIds(userId);
                    }
                    syncMsg.setValue(campData.getRedPacketValue());
                    MessageHelper.sendPacket(serverId, 0, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_RED_PACKET_BOX_VALUE_SYNC, syncMsg));
                }
            }
        }
    }

    private Map<Long, List<Long>> getCampServerIdUserIdMap(int campId) {
        Map<Long, List<Long>> resMap = new ConcurrentHashMap<>();
        for (CrossChuHanUserData userData : new ConcurrentHashMap<>(userDataMap).values()) {
            if (userData.getCampId() == campId) {
                UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userData.getUserId());
                if (userBaseInfo != null) {
                    if (!resMap.containsKey(userBaseInfo.getServerId())) {
                        resMap.put(userBaseInfo.getServerId(), new ArrayList<>());
                    }
                    resMap.get(userBaseInfo.getServerId()).add(userData.getUserId());
                }
            }
        }
        return resMap;
    }

    // 新增玩家的红包宝箱信息
    public void addUserRedPacketCount(CrossChuHanRedPacket redPacket, CrossChuHanCampData campData) {
        Map<Long, CrossChuHanUserRedPacketCount> userRedPacketMap = campData.getUserRedPacketMap();
        if (!userRedPacketMap.containsKey(redPacket.getUserId())) {
            CrossChuHanUserRedPacketCount temp = new CrossChuHanUserRedPacketCount();
            temp.setUserId(redPacket.getUserId());
            userRedPacketMap.put(redPacket.getUserId(), temp);
        }
        CrossChuHanUserRedPacketCount userRedPacket = userRedPacketMap.get(redPacket.getUserId());
        Map<Integer, Integer> redPacketCountMap = userRedPacket.getUserRedPacketCountMap();
        redPacketCountMap.put(redPacket.getGoodId(), redPacketCountMap.getOrDefault(redPacket.getGoodId(), 0) + 1);
        userRedPacket.setUserRedPacketCountMap(redPacketCountMap);
    }

    /**
     * 获取红包列表（优先没领取的）
     */
    public void getRedPacketList(long userId) {
        ChuHanProto.ChuHanRedPacketGetListRespMsg.Builder respMsg = ChuHanProto.ChuHanRedPacketGetListRespMsg.newBuilder();
        respMsg.setRet(0);
        // 没资格
        if (!isAble(userId)) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_RED_PACKET_LIST % 20000, respMsg, userId);
            return;
        }
        CrossChuHanUserData userData = getUserData(userId);
        int campId = userData.getCampId();

        // 统计红包
        LinkedList<CrossChuHanRedPacket> noGetRewardList = new LinkedList<>();
        LinkedList<CrossChuHanRedPacket> getRewardList = new LinkedList<>();
        CrossChuHanCampData campData = campDataMap.get(campId);
        if (campData != null) {
            for (CrossChuHanRedPacket redPacket : campData.getRedPacketMap().values()) {
                if (redPacket.getGetRewardUserList().contains(userId)) {
                    getRewardList.add(redPacket);
                } else {
                    noGetRewardList.add(redPacket);
                }
            }
        }

        // 发送晚的在前
        noGetRewardList.sort(new RedPacketSort());
        getRewardList.sort(new RedPacketSort());

        // 拿前50个
        int maxNum = 50;
        int num = 1;
        for (CrossChuHanRedPacket redPacket : noGetRewardList) {
            if (num >= maxNum) {
                break;
            }
            respMsg.addRedPackets(parseChuHanRedPacketMsg(redPacket, false));
            num++;
        }
        for (CrossChuHanRedPacket redPacket : getRewardList) {
            if (num >= maxNum) {
                break;
            }
            respMsg.addRedPackets(parseChuHanRedPacketMsg(redPacket, true));
            num++;
        }

        sendPacket(Protocol.S_CHUHAN_RED_PACKET_LIST % 20000, respMsg, userId);
    }

    public void chooseBirthCity(long userId, int cityId) {
        ChuHanProto.ChuHanChooseBirthCityRespMsg.Builder resp = ChuHanProto.ChuHanChooseBirthCityRespMsg.newBuilder();
        resp.setRet(0);
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_NO_ABLE), userId);
            return;
        }

        if (userData.getBirthCityId() == cityId) {
            sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_CHOOSE_BIRTH_CITY_REPEAT), userId);
            return;
        }
        long currentTime = DateHelper.getCurrentTime();
        if (userData.getFlitTime() > 0 && userData.getFlitTime() + config.getCHUHAN_MOVE_CITY_CD() * DateHelper.HOUR_MILLIONS > currentTime) {
            sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_CHOOSE_BIRTH_CITY_CD), userId);
            return;
        }

        ChuHanCityConfig toCityConfig = config.getCityConfigMap().get(cityId);
        if (toCityConfig == null) {
            sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_CITY_NO_EXIST), userId);
            return;
        }

        if (toCityConfig.getMapCamp() == 0 || (toCityConfig.getType() != eChuHanCityType.Type2_DuCity.getValue() && toCityConfig.getType() != eChuHanCityType.Type1_MainCity.getValue())) {
            sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_CITY_TYPE_ERR), userId);
            return;
        }

        if (!getConfig().isInChooseBirthCityTime(userData.getCampId(), toCityConfig.getMapCamp())) {
            sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_NO_OPERATE_TIME), userId);
            return;
        }

//        List<Integer> cityRouteList = getRouteList(userData.getUserId(), getCampBirthCity(userData.getCampId()), cityId);
//        if (cityRouteList == null || cityRouteList.size() <= 1) {
//            sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_NO_ROUTE), userId);
//            return;
//        }

        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
        if (city == null) {
            sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_CITY_NO_EXIST), userId);
            return;
        }

        synchronized (lock) {
            if (city.getCampId() != userData.getCampId()) {
                sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_CHOOSE_BIRTH_CITY_CAMP_ERR), userId);
                return;
            }
            if (city.getBattlefield() != null) {
                sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp.setRet(GameErrorCode.E_CHUHAN_CITY_IN_BATTLE), userId);
                return;
            }

            userData.setBirthCityId(cityId);
            userData.setFlitTime(currentTime);
        }

        resp.setHomeCityId(cityId);
        resp.setFlitTime(currentTime);
        sendPacket(Protocol.S_CHUHAN_CHOOSE_BIRTH_CITY % 20000, resp, userId);

        CrossAutoLogMgr.add(new LogChuHanChooseBirthCity(
                userData.getActivityId(),
                userData.getUserId(),
                userData.getGroupId(),
                userData.getCampId(),
                cityId
        ));
    }

    /**
     * 刷新每日的指定军令城池,一天检查一次
     */
    public void refreshOrderCity() {
        if (DateHelper.getCurrentTime()/1000 >= getConfig().getActivityInfo().getEndTime()) {//活动结束了就不要跑任务了
            return;
        }
        int day = ActivityHelper.getWhichDayAfterBeginTime(getConfig().getActivityInfo());
        if (day <= 0) {
            return;
        }

        //更新阵营本日占领的城池记录
        for (CrossChuHanCampData campData : campDataMap.values()) {
            int campId = campData.getCampId();

            for (CrossChuHanCityData city : crossChuHanData.getCityMap().values()) {
                if (city.getCampId() == campId) {
                    campData.addDayOccupyCity(day, city.getCityId());
                }
            }
        }

        CrossChuHanOrderCity orderCity = crossChuHanData.getOrderCityMap().get(day);
        if (orderCity != null) {
            return;
        }

        Map<Integer, List<Integer>> cityMap = getConfig().randomOrderCityMap();

        orderCity = new CrossChuHanOrderCity();
        orderCity.setDay(day);
        orderCity.setOrderCityMap(cityMap);
        crossChuHanData.getOrderCityMap().put(day, orderCity);

        //同步一下玩家
        for (CrossChuHanUserData userData : userDataMap.values()) {
            syncOrderCityTask(day, userData);
        }
    }

    public void getPveReport(long userId, int cityType) {
        ChuHanProto.ChuHanGetPveReportRespMsg.Builder resp = ChuHanProto.ChuHanGetPveReportRespMsg.newBuilder();
        resp.setRet(0);
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            sendPacket(Protocol.S_CHUHAN_GET_PVE_REPORT % 20000, resp.setRet(GameErrorCode.E_CHUHAN_NO_ABLE), userId);
            return;
        }

        if (cityType != eChuHanCityType.Type7_BeiJing.getValue() && cityType != eChuHanCityType.Type8_NanYang.getValue()) {
            sendPacket(Protocol.S_CHUHAN_GET_PVE_REPORT % 20000, resp.setRet(GameErrorCode.E_CHUHAN_CITY_TYPE_ERR), userId);
            return;
        }

        ChuHanProto.ChuHanPveReportTemp.Builder reportBuilder = ChuHanProto.ChuHanPveReportTemp.newBuilder();
        reportBuilder.setCityType(cityType);

        Map<Integer, CrossChuHanUserData> firstUserMap = pveCampFirstUserDataMap.get(cityType);
        if (firstUserMap != null) {
            for (Map.Entry<Integer, CrossChuHanUserData> entry : firstUserMap.entrySet()) {
                int campId = entry.getKey();
                CrossChuHanCampData campData = campDataMap.get(campId);

                CrossChuHanUserData firstUserData = entry.getValue();
                ChuHanProto.ChuHanPveFirstUserTemp.Builder firstBuilder = ChuHanProto.ChuHanPveFirstUserTemp.newBuilder();
                firstBuilder.setUserId(firstUserData.getUserId());
                firstBuilder.setCampId(campId);
                UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(firstUserData.getUserId());
                firstBuilder.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
                firstBuilder.setUserPveZhanGong(firstUserData.getPveZhanGong(cityType));
                firstBuilder.setCampPveZhanGong(campData.getPveZhanGong(cityType));
                reportBuilder.addFirstUserList(firstBuilder);
            }
        }

        reportBuilder.setUserPveZhanGong(userData.getPveZhanGong(cityType));
        reportBuilder.setWinCampId(getPveWinnerCampId(cityType));
        resp.setReport(reportBuilder);

        sendPacket(Protocol.S_CHUHAN_GET_PVE_REPORT % 20000, resp, userId);
    }

    public void pveSettleTask(int cityType) {
        checkPveCitySettle(cityType);
    }

    private void checkPveCitySettle(int cityType) {
        long currentTime = DateHelper.getCurrentTime();
        long pveCityEndTime = getConfig().getPveCitySettleTime(cityType);
        if (currentTime < pveCityEndTime) {
            addTask(new CrossChuHanPveSettleTask(this, pveCityEndTime, cityType));
        } else {
            synchronized (lock) {
                int pveWinnerCampId = getPveWinnerCampId(cityType);
                for (CrossChuHanCampData campData : campDataMap.values()) {
                    int pveBuff = campData.getPveBuff(cityType);
                    if (pveBuff == 0) {//没有结算过BUFF
                        pveBuff = getConfig().getPveRewardBuff(cityType, pveWinnerCampId == campData.getCampId());
                        campData.setPveBuff(cityType, pveBuff);
                    }
                    refreshCampBuff(campData.getCampId());

                    afterBeginBuff(pveBuff, campData.getCampId());
                    isNeedSyncBuff.set(true);
                }
            }
        }
    }

    public void crossChuHanMoveToTargetTask() {
        if (DateHelper.getCurrentTime() / 1000 >= getConfig().getActivityInfo().getEndTime()) {//活动结束了就不要跑任务了
            return;
        }

        for (CrossChuHanUserData userData : userDataMap.values()) {
            for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                if (team.getTargetCityId() > 0) {
                    synchronized (lock) {
                        handleTargetCity(team);
                    }
                }
            }
        }

        addTask(new CrossChuHanMoveToTargetTask(this, DateHelper.getCurrentTime() + 5 * DateHelper.SECOND_MILLIONS));
    }

    public void updateUnionMemberByCmd(ChuHanProto.CrossChuHanGetMemberRespMsg reqMsg) {
        for (ChuHanProto.ChuHanUnionMemberMsg memberMsg : reqMsg.getMemberList()) {
            CrossChuHanUserData userData = getUserData(memberMsg.getUserId());
            if (userData == null) {
                continue;
            }
            CrossChuHanUnionJoin join = getUnionJoin(userData.getUnionUid());
            if (join == null) {
                continue;
            }
            CrossChuHanUnionJoinMember member = join.getMemberMap().get(memberMsg.getUserId());
            member.setEarnSpeed(Long.valueOf(memberMsg.getEarnSpeed()));
            join.setUpdateOption();
        }

    }

    public void getToCityRouteList(long userId, int toCityId) {
        ChuHanProto.ChuHanGetTeamRouteListRespMsg.Builder respMsg = ChuHanProto.ChuHanGetTeamRouteListRespMsg.newBuilder();
        respMsg.setRet(0);
        respMsg.setCityId(toCityId);
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            sendPacket(Protocol.S_CHUHAN_GET_TEAM_ROUTE_LIST % 20000, respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE), userId);
            return;
        }

        if(!checkCityCanSetTarget(userData.getCampId(), toCityId)){
            sendPacket(Protocol.S_CHUHAN_GET_TEAM_ROUTE_LIST % 20000, respMsg.setRet(GameErrorCode.E_CHUHAN_CAN_NOT_SET_TARGET_CITY), userId);
            return;
        }

        for (CrossChuHanTeam team : userData.getTeamMap().values()) {
            eTongBingTeamStatus teamStatus = getTeamStatus(team);
            if (teamStatus != eTongBingTeamStatus.Free) {
                continue;
            }
            if (team.getCityId() == toCityId) {
                continue;
            }
            List<Integer> routeList = getRouteListNew(userId, team.getCityId(), toCityId);
            ChuHanProto.ChuHanGetTeamRouteTemp.Builder teamBuilder = ChuHanProto.ChuHanGetTeamRouteTemp.newBuilder();
            teamBuilder.setTeamId(team.getOccupation());
            teamBuilder.addAllCityIds(routeList);
            respMsg.addTeamRouteInfo(teamBuilder);
        }

        sendPacket(Protocol.S_CHUHAN_GET_TEAM_ROUTE_LIST % 20000, respMsg, userId);
    }

    class RedPacketSort implements Comparator<CrossChuHanRedPacket> {
        @Override
        public int compare(CrossChuHanRedPacket o1, CrossChuHanRedPacket o2) {
            return Long.compare(o2.getTime(), o1.getTime());
        }
    }

    /**
     * 初始化各阵营红包排行
     */
    public void initUserRedPacketNumMap() {
        for (CrossChuHanCampData campData : campDataMap.values()) {
            Map<Long, CrossChuHanUserRedPacketCount> tempMap = new ConcurrentHashMap<>();
            for (CrossChuHanRedPacket redPacket : campData.getRedPacketMap().values()) {
                if (!tempMap.containsKey(redPacket.getUserId())) {
                    CrossChuHanUserRedPacketCount temp = new CrossChuHanUserRedPacketCount();
                    temp.setUserId(redPacket.getUserId());
                    tempMap.put(redPacket.getUserId(), temp);
                }
                CrossChuHanUserRedPacketCount userRedPacketCount = tempMap.get(redPacket.getUserId());
                Map<Integer, Integer> redPacketCountMap = userRedPacketCount.getUserRedPacketCountMap();
                redPacketCountMap.put(redPacket.getGoodId(), redPacketCountMap.getOrDefault(redPacket.getGoodId(), 0) + 1);
                userRedPacketCount.setUserRedPacketCountMap(redPacketCountMap);
            }
            campData.setUserRedPacketMap(tempMap);
        }
    }


    /**
     * 红包发送记录
     */
    public void getRedPacketRecord(long userId) {
        ChuHanProto.ChuHanRedPacketGetRecordRespMsg.Builder respMsg = ChuHanProto.ChuHanRedPacketGetRecordRespMsg.newBuilder();
        respMsg.setRet(0);

        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_RED_PACKET_RECORD_LIST % 20000, respMsg, userId);
            return;
        }

        CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
        if (campData != null) {
            Map<Long, CrossChuHanUserRedPacketCount> userRedPacketMap = campData.getUserRedPacketMap();
            for (CrossChuHanUserRedPacketCount userRedPacket : userRedPacketMap.values()) {
                UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userRedPacket.getUserId());
                if (userBaseInfo == null) {
                    userBaseInfo = new UserBaseInfo();
                }

                ChuHanProto.ChuHanRedPacketRankMsg.Builder rankTempMsg = ChuHanProto.ChuHanRedPacketRankMsg.newBuilder();
                rankTempMsg.setUserId(userRedPacket.getUserId());
                rankTempMsg.setNickName(userBaseInfo.getNickName());
                for (Map.Entry<Integer, Integer> entry : userRedPacket.getUserRedPacketCountMap().entrySet()) {
                    ChuHanProto.ChuHanRedPacketBoxMsg.Builder boxTempMsg = ChuHanProto.ChuHanRedPacketBoxMsg.newBuilder();
                    boxTempMsg.setBoxKey(entry.getKey());
                    boxTempMsg.setNum(entry.getValue());
                    rankTempMsg.addBox(boxTempMsg);
                }
                respMsg.addRecords(rankTempMsg);
            }
        }

        sendPacket(Protocol.S_CHUHAN_RED_PACKET_RECORD_LIST % 20000, respMsg, userId);
    }


    /**
     * 领取阵营宝箱
     */
    public void getRedPacketBox(long userId, ChuHanProto.CrossChuHanRedPacketGetBoxReqMsg reqMsg) {
        ChuHanProto.CrossChuHanRedPacketGetBoxRespMsg.Builder serverMsg = ChuHanProto.CrossChuHanRedPacketGetBoxRespMsg.newBuilder();
        serverMsg.setRet(0);

        // 资格
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            serverMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(GameProtocol.S_CROSS_CHUHAN_RED_PACKET_GET_BOX, serverMsg, userId);
            return;
        }

        // 数据判断
        CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
        if (campData == null) {
            serverMsg.setRet(GameErrorCode.E_CHUHAN_RED_PACKET_IS_GET);
            sendPacket(GameProtocol.S_CROSS_CHUHAN_RED_PACKET_GET_BOX, serverMsg, userId);
            return;
        }

        long boxNo = StringUtils.isNullOrEmpty(reqMsg.getOrderNo()) ? 0 : Long.valueOf(reqMsg.getOrderNo());
        Map<Integer, ChuHanCampBoxConfig> campBoxConfigMap = config.getCampBoxConfigMap();
        Map<Long, CrossChuHanRedPacket> redPacketMap = campData.getRedPacketMap();

        synchronized (lock) {

            if (boxNo == 0) {
                // 【一键领取】
                Property reward = new Property();
                for (CrossChuHanRedPacket redPacket : redPacketMap.values()) {
                    Set<Long> getRewardUserList = redPacket.getGetRewardUserList();
                    if (!getRewardUserList.contains(userId)) {
                        ChuHanCampBoxConfig campBoxConfig = campBoxConfigMap.get(redPacket.getGoodId());
                        reward.addProperty(campBoxConfig.getBoxId(), BigInteger.ONE);
                        getRewardUserList.add(userId);
                        campData.setRedPacketValue(campData.getRedPacketValue() + campBoxConfig.getBoxScore());
                    }
                }
                String rewardStr = PropertyHelper.parsePropertyToString(reward);
                if (StringUtils.isNullOrEmpty(rewardStr)) {
                    serverMsg.setRet(GameErrorCode.E_CHUHAN_RED_PACKET_IS_GET);
                    sendPacket(GameProtocol.S_CROSS_CHUHAN_RED_PACKET_GET_BOX, serverMsg, userId);
                    return;
                }
                serverMsg.setReward(rewardStr);
                sendPacket(GameProtocol.S_CROSS_CHUHAN_RED_PACKET_GET_BOX, serverMsg, userId);
                campData.setUpdateOption();
            } else {
                // 【单个领取】
                CrossChuHanRedPacket redPacket = redPacketMap.get(boxNo);
                if (redPacket == null) {
                    serverMsg.setRet(GameErrorCode.E_CHUHAN_RED_PACKET_NO_EXIT);
                    sendPacket(GameProtocol.S_CROSS_CHUHAN_RED_PACKET_GET_BOX, serverMsg, userId);
                    return;
                }
                Set<Long> getRewardList = redPacket.getGetRewardUserList();
                if (getRewardList.contains(userId)) {
                    serverMsg.setRet(GameErrorCode.E_CHUHAN_RED_PACKET_IS_GET);
                    sendPacket(GameProtocol.S_CROSS_CHUHAN_RED_PACKET_GET_BOX, serverMsg, userId);
                    return;
                }
                ChuHanCampBoxConfig campBoxConfig = campBoxConfigMap.get(redPacket.getGoodId());
                getRewardList.add(userId);
                campData.setRedPacketValue(campData.getRedPacketValue() + campBoxConfig.getBoxScore());
                serverMsg.setReward(campBoxConfig.getBoxId() + "=1");
                // 一键领取的时候还要下去连大宝箱都领了
                serverMsg.setBoxValue(campData.getRedPacketValue());
                serverMsg.setActivityId(config.getActivityInfo().getActivityId());
                sendPacket(GameProtocol.S_CROSS_CHUHAN_RED_PACKET_GET_BOX, serverMsg, userId);
                campData.setUpdateOption();
            }
        }
    }

    /**
     * 计算全部兵营
     */
    public void calcAllBarracks() {
        for (CrossChuHanUserData userData : userDataMap.values()) {

            CrossChuHanBarracks barracks = userData.getBarracks();
            if (barracks.getReadyMaxNum() > 0) {

                barracksCalc(userData.getUserId());

                // 红点
                if (barracks.getCreateNum() == 0 && barracks.getReadyNum() < barracks.getReadyMaxNum()) {
                    addRedDot(userData, eTongBingRedDotType.Type4_BarracksFree);
                }
                if (barracks.getWoundedNum() == 0 && barracks.getRescueNum() > 0
                        || barracks.getRescueNum() >= config.getRedDotCureNum()) {
                    addRedDot(userData, eTongBingRedDotType.Type6_Wounded);
                }
            }
        }
    }

    /**
     * 重新计算最大兵营数，来触发时候要有buff，开始和失效时候触发 Type6_ReadySoldierMaxNum
     */
    private void recalculateBarracksReadyMaxNum(long userId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        CrossChuHanBarracks barracks = userData.getBarracks();
        barracks.setReadyMaxNum(getBarracksReadyMaxNum(userId));
        userData.setUpdateOption();
    }

    /**
     * 重新计算城池产量，开始的时候buff不能有，结束的时候buff不能失效 Type5_CityFoodProduce
     *
     * @param campId
     * @param dealTime buff开始或者结束的时间点
     */
    private void recalculateCityFoodProdProduction(int campId, long dealTime) {
        makeAllCityCollect(campId, dealTime);
    }

    /**
     * 重新计算医院最大伤兵数，来触发时候要有buff，开始和失效时候触发 Type4_Hospital
     *
     * @param userId
     */
    private void recalculateBarracksHospitalWoundedMaxNum(long userId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        CrossChuHanBarracks barracks = userData.getBarracks();
        barracks.setHospitalSize(getBarracksWoundedMaxNum(userId));
        userData.setUpdateOption();
    }

    /**
     * 重新计算每次治疗伤兵数
     */
    private void recalculateBarracksHospitalWoundedCount(long userId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        CrossChuHanBarracks barracks = userData.getBarracks();
        barracks.setRescueCount(getBarracksWoundedCount(userId));
        userData.setUpdateOption();
    }

    /**
     * 重新计算医院恢复伤兵，开始的时候buff不能有，结束的时候buff不能失效 Type4_Hospital
     */
    private void recalculateBarracksHospitalRecovery(long userId) {
        barracksCalc(userId);
    }

    /**
     * 计算军营预备兵上限
     */
    private long getBarracksReadyMaxNum(long userId) {
        // 拿队伍带兵上限总和
        long allValue = 0L;
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return 0;
        }
        for (CrossChuHanTeam team : userData.getTeamMap().values()) {
            allValue = allValue + team.getMaxSoldier();
        }
        // 乘以参数
        allValue = allValue * config.getBarrackLimitParam() / 1000;
        // 加buff
        Integer buffAddition = getBuffAddition(userData.getUserId(), eChuHanBuffType.Type6_ReadySoldierMaxNum).get(0);
        allValue = BigDecimal.valueOf(allValue)
                .multiply(BigDecimal.valueOf(1000 + buffAddition)).divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN).longValue();
        return allValue;
    }

    //获取最大的军营医院人数
    private long getBarracksWoundedMaxNum(long userId) {
        long allValue = config.getWoundedSoldierMaxNum();
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return 0;
        }
        Integer buffAddition = getBuffAddition(userData.getUserId(), eChuHanBuffType.Type4_Hospital).get(0);
        allValue = BigDecimal.valueOf(allValue)
                .multiply(BigDecimal.valueOf(1000 + buffAddition)).divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN).longValue();
        return allValue;
    }

    // 获得伤兵恢复个数
    private long getBarracksWoundedCount(long userId) {
        //伤兵加上buff修复速度
        Integer buffAddition = getBuffAddition(userId, eChuHanBuffType.Type4_Hospital).get(1);
        return BigDecimal.valueOf(config.getWoundedRescueCount())
                .multiply(BigDecimal.valueOf(1000 + buffAddition))
                .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN).longValue();
    }

    public CrossChuHanUserData getUserData(long userId) {
        CrossChuHanUserData userData = userDataMap.get(userId);
        return userData;
    }

    public CrossChuHanUnionJoin getUnionJoin(String unionUid) {
        return unionJoinMap.get(unionUid);
    }

    public CrossChuHanData getCrossChuHanData() {
        return crossChuHanData;
    }

    public int getGroupId() {
        return groupId;
    }

    // ============================== 定时器 ==============================

    /**
     * 初始化所有Task
     */
    public void initTask() {

        // 空事件
        addTask(new CrossChuHanEmptyTask(this, System.currentTimeMillis() + DateHelper.SECOND_MILLIONS * 10));

        // 商会报名锁定
        long checkLockTime = Math.min(System.currentTimeMillis(), config.getJoinTimeList().get(1)); // 锁名单前，要检查下其他数据
        addTask(new CrossChuHanLockUnionTask(this, checkLockTime));

        // 阶段切换移动玩家
        long stage1Begin = config.getStage1TimeList().get(0).get(0);
        addTask(new CrossChuHanInitTeamPosTask(this, stage1Begin));

        // 军令城池刷新
        long cityRefreshTime = config.getOrderCityRefreshTime();
        addTask(new CrossChuHanRefreshOrderCityTask(this, cityRefreshTime));

        addTask(new CrossChuHanAlwaysSyncTask(this, System.currentTimeMillis() + DateHelper.SECOND_MILLIONS * 5));

        // 战斗事件
        for (CrossChuHanCityData city : getCrossChuHanData().getCityMap().values()) {
            if (city.getBattlefield() != null) {
                addTask(new CrossChuHanBattleTask(this, calcNextBattleTime(city), city.getCityId()));
            }
        }

        // 移动事件
        for (CrossChuHanUserData userData : userDataMap.values()) {
            for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                if (!team.getRouteList().isEmpty()) {
                    addTask(new CrossChuHanMoveTask(this, team.getArriveTime(), userData.getUserId(), team.getOccupation()));
                }
            }
        }

        // 城防军恢复
        for (CrossChuHanCityData city : crossChuHanData.getCityMap().values()) {
            if (city.getTeamRecoveryBeginTime() != 0) {
                addTask(new CrossChuHanCityTeamRecoveryTask(this, city.getTeamRecoveryBeginTime() + config.getCityTeamRecoveryTime(), city.getCityId()));
            }
            if (city.getCampId() != 0) {
                addTask(new CrossChuHanRecoveryExtraTeamTask(this, getNextRecoveryExtraTeamTime(city), city.getCityId()));
            }
        }

        // 发商会荣誉（可重复调用）
        addTask(new CrossChuHanSendUnionHonorTask(this, config.getActivityInfo().getEndTime() * 1000L + DateHelper.MINUTE_MILLIONS * 5));

        // 同步移动数据
        addTask(new CrossChuHanSyncTeamTask(this));

        // 同步城池数据
        addTask(new CrossChuHanSyncCityTask(this));

        // 设置城主
        addTask(new CrossChuHanSetWinnerTask(this, config.getActivityInfo().getEndTime() * 1000L));

        //pve结算
        addTask(new CrossChuHanPveSettleTask(this, DateHelper.getCurrentTime() + 5 * DateHelper.SECOND_MILLIONS, eChuHanCityType.Type7_BeiJing.getValue()));
        addTask(new CrossChuHanPveSettleTask(this, DateHelper.getCurrentTime() + 5 * DateHelper.SECOND_MILLIONS, eChuHanCityType.Type8_NanYang.getValue()));

        //自动移动到战略地点
        addTask(new CrossChuHanMoveToTargetTask(this, DateHelper.getCurrentTime() + 5 * DateHelper.SECOND_MILLIONS));
    }

    /**
     * 空事件，10秒一次
     */
    public void crossChuHanEmptyTask() {
        addTask(new CrossChuHanEmptyTask(this, System.currentTimeMillis() + DateHelper.SECOND_MILLIONS * 10));

//        setAllRank();
    }

    /**
     * 商会报名、锁名单 任务
     */
    public void crossChuHanLockUnionTask() {
        // 【已经锁名单了】
        if (getCrossChuHanData().isLockUnion()) {
            return;
        }

        long now = System.currentTimeMillis();

        // 报名期结束 锁定商会名单
        if (now > config.getJoinTimeList().get(1)) {
            logger.error("楚汉争霸请求榜单锁定");
            List<Long> serverList = config.getActivityInfo().getServerIdList();
            int index = randomHelper.next(0, serverList.size());
            long serverId = serverList.get(index);
            //报名期结算就去拉取有资格的商会
            MessageHelper.sendPacket(serverId, 0,
                    YanQuMessageUtils.buildMessage(GameProtocol.S_PEAK_UNION_MAX_ABILITY_RANK_LOCK,
                            CommonActivityProto.CommonActivityRankListReqMsg.newBuilder().setGroupId(groupId).setActivityId(config.getActivityInfo().getActivityId())));
            //需要快速请求本服的判断两秒就算过期的
            long next10Second = System.currentTimeMillis() + DateHelper.SECOND_MILLIONS * 10;
            addTask(new CrossChuHanLockUnionTask(this, next10Second));
        } else {
            // 【没锁名单】先把任务加回去比较稳
            long next10Second = System.currentTimeMillis() + DateHelper.SECOND_MILLIONS * 10;
            addTask(new CrossChuHanLockUnionTask(this, next10Second));
        }
    }

    /**
     * 初始化队伍位置
     */
    public void crossChuHanInitTeamPosTask() {
        long now = System.currentTimeMillis();

        // 【没锁榜】过会儿再看看
        if (!getCrossChuHanData().isLockUnion()) {
            addTask(new CrossChuHanInitTeamPosTask(this, now + DateHelper.SECOND_MILLIONS * 10));
            return;
        }

        // 【强制移动没city的队伍】
        for (CrossChuHanUserData userData : userDataMap.values()) {
            if (userData.getCampId() == 0) {
                continue; // 没阵营跳过
            }
            for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                if (team.getCityId() == 0) {
                    int birthCityId = getBirthCityId(userData);
                    if (birthCityId == 0) {
                        continue;
                    }
                    team.setRouteList(Collections.singletonList(birthCityId));
                    forceArriveNextCity(team, System.currentTimeMillis());
                    userData.setUpdateOption();
                }
            }
        }

        // 【定时检查一下】
        addTask(new CrossChuHanInitTeamPosTask(this, now + DateHelper.MINUTE_MILLIONS * 5));
    }

    /**
     * 战斗任务
     */
    public void crossChuHanBattleTask(int cityId) {
        synchronized (lock) {

            // 是否没战斗了
            CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
            CrossChuHanBattlefield battlefield = city.getBattlefield();
            if (battlefield == null) {
                return;
            }

            // 时间到了没有
            long battleTime = calcNextBattleTime(city);
            if (System.currentTimeMillis() < battleTime) {
                addTask(new CrossChuHanBattleTask(this, battleTime, cityId));
                return;
            }

            // 处理战斗
            List<CrossChuHanBattleResult> resultList = new ArrayList<>();
            for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                CrossChuHanBattleResult battleResult = handleBattle(city, battlefield, part, battleTime);
                if (battleResult != null) {
                    resultList.add(battleResult);
                }
            }

            // 设置上次战斗时间
            int drumTimes = battlefield.getDrumTimes();
            if (drumTimes > 0) {
                battlefield.setDrumTimes(drumTimes - 1);
            }
            city.setBattledNum(city.getBattledNum() + resultList.size());
            battlefield.setBattledNum(battlefield.getBattledNum() + resultList.size());
            battlefield.setLastBattleTime(battleTime);

            // 士兵进入战场
            teamToBattlefield(city, false);

            // 移除空战场
            List<Integer> removePartId = removeEmptyBattlefield(city);

            // 判断城池战报
            CrossChuHanCityWarReport cityWarReport = null;
            if (battlefield.getPartMap().isEmpty()) {
                cityWarReport = recordBattleReport(city, battleTime); // 记录战报
            }

            // 同步战斗结果
            syncBattleResultMsg(resultList, cityWarReport);

            // 战斗结束处理
            if (cityWarReport != null) {
                handleBattleFinish(city, battleTime); // 判断城池换主人
                city.setBattlefield(null); // 移除战场
                chatLogic.clearBattlefieldChat(cityId); // 清空战场聊天
            }

            // 判断新的大战场
            if (city.getAtkTeamList().size() > 0 && city.getBattlefield() == null) {
                createBattlefield(city);
            }

            // 是否继续战斗
            if (city.getBattlefield() != null) {
                addTask(new CrossChuHanBattleTask(this, calcNextBattleTime(city), city.getCityId()));
            } else {
                syncCityTeamToSelf(city);
            }

            city.setUpdateOption();
            crossChuHanData.setUpdateOption();

            // 同步城池
            addNeedSyncCity(cityId);

            // 清空场景
            for (Integer partId : removePartId) {
                CrossSceneMgr.removeScene(config.getActivityInfo().getActivityId(), eSceneType.TongBingBattlefield, getBattlefieldSceneKey(cityId, partId));
            }
        }
    }

    /**
     * 移动任务
     */
    public void crossChuHanMoveTask(long userId, int teamId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        CrossChuHanTeam team = userData.getTeamMap().get(teamId);
        synchronized (lock) {

            // 判断状态
            if (getTeamStatus(team) != eTongBingTeamStatus.Move) {
                return;
            }

            long now = System.currentTimeMillis();

            // 时间没到
            if (now < team.getArriveTime()) {
                addTask(new CrossChuHanMoveTask(this, team.getArriveTime(), userId, teamId));
                return;
            }

            // 队伍到达城池
            forceArriveNextCity(team, team.getArriveTime());
        }
    }


//    /**
//     * 过期失效buff
//     */
//    public void crossChuHanBuffExpireTask() {
//        long now = System.currentTimeMillis();
//        long minEndTime = Long.MAX_VALUE;
//        Map<Integer, List<CrossChuHanCampBuff>> campBuffMap = getCrossChuHanData().getCampBuffMap();
//        for (Map.Entry<Integer, List<CrossChuHanCampBuff>> entry : campBuffMap.entrySet()) {
//            List<CrossChuHanCampBuff> buffList = entry.getValue();
//            for (int i = 0; i < buffList.size(); i++) {
//                CrossChuHanCampBuff campBuff = buffList.get(i);
//                long endTime = getBuffEndTime(campBuff);
//                if (now >= endTime) {
//                    // 结束buff
//                    beforeEndBuff(campBuff);
//                    buffList.remove(i--);
//                    afterEndBuff(campBuff);
//                } else {
//                    minEndTime = Math.min(endTime, minEndTime);
//                }
//            }
//        }
//        if (minEndTime != Long.MAX_VALUE) {
//            addTask(new CrossChuHanBuffExpireTask(this, minEndTime));
//        }
//
//        getCrossChuHanData().setUpdateOption();
//    }

    /**
     * 城防军恢复
     */
    public void crossChuHanCityTeamRecoveryTask(int cityId) {
        synchronized (lock) {

            long now = System.currentTimeMillis();

            CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
            if (city.getTeamRecoveryBeginTime() == 0) {
                // 恢复过了，或者被刷新了
                return;
            }

            long recoveryTime = city.getTeamRecoveryBeginTime() + config.getCityTeamRecoveryTime();
            if (now < recoveryTime) {
                // 时间还没到
                addTask(new CrossChuHanCityTeamRecoveryTask(this, recoveryTime, cityId));
                return;
            }

            if (city.getBattlefield() != null) {
                // 正在战斗
                city.setTeamRecoveryBeginTime(0);
                return;
            }

            ChuHanCityConfig cityConfig = config.getCityConfigMap().get(cityId);
            ChuHanCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap().get(cityConfig.getType());

            int num = cityTypeConfig.getTeamNum() + getBuffAddition(city.getCampId(), eChuHanBuffType.Type3_CityTeamMaxNum).get(0);

            // 恢复
            if (city.getTeamNum() < num) {
                city.setTeamNum(num);
            }
            city.setTeamRecoveryBeginTime(0);
            // 战斗场次清空
            city.setBattledNum(0);
            getCrossChuHanData().setUpdateOption();
        }
    }

    /**
     * 获取下次恢复额外守军的时间
     */
    private long getNextRecoveryExtraTeamTime(CrossChuHanCityData city) {
        // 间隔一小时
        long lastRecoveryExtraTeamTime = city.getLastRecoveryExtraTeamTime() == 0 ? config.getStage1TimeList().get(0).get(0) : city.getLastRecoveryExtraTeamTime();
        return lastRecoveryExtraTeamTime + DateHelper.HOUR_MILLIONS;
    }

    /**
     * 恢复额外守军
     */
    public void crossChuHanRecoveryExtraTeamTask(int cityId) {
        synchronized (lock) {
            long now = System.currentTimeMillis();
            CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
            long recoveryTime = getNextRecoveryExtraTeamTime(city);
            if (now < recoveryTime) {
                // 时间还没到
                addTask(new CrossChuHanRecoveryExtraTeamTask(this, recoveryTime, cityId));
                return;
            }
            // 恢复额外守军
            int cityType = config.getCityConfigMap().get(cityId).getType();
            ChuHanCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap().get(cityType);
            int extraTeamNum = cityTypeConfig.getExtraTeamNum();
            int extraTeamNumMax = cityTypeConfig.getExtraTeamNumMax();
            int addNum = 0;
            if (extraTeamNumMax > 0) {
                if (city.getExtraTeamNum() + extraTeamNum > extraTeamNumMax) {
                    addNum = extraTeamNumMax - city.getExtraTeamNum();
                } else {
                    addNum = extraTeamNum;
                }
            }
            city.setExtraTeamNum(city.getExtraTeamNum() + addNum);
            city.setLastRecoveryExtraTeamTime(recoveryTime);
            city.setUpdateOption();
            getCrossChuHanData().setUpdateOption();
            // 恢复的守军进入战场
            if (city.getBattlefield() != null) {
                teamToBattlefield(city, true);
            }
            // 下一次恢复时间
            addTask(new CrossChuHanRecoveryExtraTeamTask(this, getNextRecoveryExtraTeamTime(city), city.getCityId()));
            logger.info("额外城防军恢复,cityId: {},当前额外城防军数量: {}", cityId, city.getExtraTeamNum());
        }
    }


    /**
     * 加商会旗帜
     */
    public void crossChuHanSendUnionHonorTask() {
        synchronized (lock) {
            long now = System.currentTimeMillis();

            // 超过时间了
            if (now > config.getActivityInfo().getEndShowTime() * 1000L) {
                return;
            }

            long getRewardTime = config.getActivityInfo().getEndTime() * 1000L + DateHelper.MINUTE_MILLIONS * 5;

            // 没到时间
            if (now < getRewardTime) {
                addTask(new CrossChuHanSendUnionHonorTask(this, getRewardTime));
                return;
            }

            // 没人
            LinkedList<CrossChuHanRank> rankList = getCampScoreRankList();
            if (rankList.isEmpty()) {
                logger.error("getCampScoreRankList is empty, can not add union honor !!!!!!!!!!");
                return;
            }

            // 没商会
            int campId = Integer.valueOf(rankList.getFirst().getId());
            CrossChuHanRankListModel rankListModel = campUnionRankMap.get(campId);
            if (rankListModel == null || rankListModel.getRankList().isEmpty()) {
                logger.error("campUnionRankMap campId {} is null or empty, can not add union honor !!!!!!!!!!", campId);
                return;
            }

            // 可以发了
            /*ActivityInfo activityInfo = config.getActivityInfo();
            List<Long> serverIdList = activityInfo.getServerIdList();
            //找出最多要前几名领奖
            int lowestRewardRank = CrossUnionHonorsActivityMgr.getLowestRewardRank(serverIdList.size(), eHonorsActivityType.UNION_HONORS_CHUHAN);
            // 可以发了 找出前n名商会
            List<String> topUnions = getTopUnions(lowestRewardRank);

            CrossUnionHonorsActivityMgr.add(
                    activityInfo.getActivityId(),
                    topUnions,
                    eActivityType.ChuHanActivity.getValue(),
                    1,
                    serverIdList,
                    serverIdList.get(0)
            );*/

        }
    }

    /**
     * 同步队伍
     */
    public void crossChuHanSyncTeamTask() {
        // 时间过了
        long nowTime = System.currentTimeMillis();
        if (nowTime > config.getActivityInfo().getEndTime() * 1000L) {
            return;
        }
        addTask(new CrossChuHanSyncTeamTask(this));
        syncNeedSyncTeam();
        syncNeedSyncBarracks();
        syncNeedSyncBaseUserData();
    }

    /**
     * 定时同步城池
     */
    public void crossChuHanSyncCityTask() {
        // 时间过了
        long nowTime = System.currentTimeMillis();
        if (nowTime > config.getActivityInfo().getEndTime() * 1000L) {
            return;
        }
        addTask(new CrossChuHanSyncCityTask(this));
        syncNeedSyncCity();
    }

    /**
     * 设置城主
     */
    public void crossChuHanSetWinnerTask() {
        // 已经设置城主
        if (crossChuHanData.getProcess2WinUserId() != 0L) {
            return;
        }
        // 时间未到
        long nowTime = System.currentTimeMillis();
        if (nowTime < config.getActivityInfo().getEndTime() * 1000L) {
            addTask(new CrossChuHanSetWinnerTask(this, config.getActivityInfo().getEndTime() * 1000L));
            return;
        }
        // 时间过了
        if (nowTime > config.getActivityInfo().getEndShowTime() * 1000L) {
            return;
        }
        // 正常设置
        Map<Integer, ChuHanCityConfig> cityConfigMap = config.getCityConfigMap();
        for (ChuHanCityConfig cityConfig : cityConfigMap.values()) {
            if (cityConfig.getType() == eChuHanCityType.Type6_CenterCity.getValue()) {
                Map<Integer, CrossChuHanCityData> cityMap = crossChuHanData.getCityMap();
                CrossChuHanCityData cityData = cityMap.get(cityConfig.getId());
                int campId = cityData.getCampId();
                long winnerUserId = 0L;
                if (campId == 0) {
                    // 没有城主
                    logger.error("tong bing activityId {} no winner!", config.getActivityInfo().getActivityId());
                    return;
                } else {
                    winnerUserId = getCampMaxZhanGongUserId(campId);
                }
                if (winnerUserId == 0L) {
                    return;
                }
                crossChuHanData.setProcess2WinUserId(winnerUserId);
                CrossChuHanUserData userData = getUserData(winnerUserId);
                if (userData == null) {
                    return;
                }
                userData.setPositionId(eTongBingPosition.Master.getValue());
                UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(winnerUserId);
                if (userBaseInfo != null) {
                    ChuHanProto.CrossChuHanCityWinerAppointRespMsg.Builder crespMsg = ChuHanProto.CrossChuHanCityWinerAppointRespMsg.newBuilder();
                    crespMsg.setWinerName("");
                    crespMsg.setPosition(userData.getPositionId());
                    crespMsg.setSendReward(true);
                    MessageHelper.sendPacket(userBaseInfo.getServerId(), winnerUserId, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_CITY_APPOINT_POSITION, crespMsg));
                }
                syncWinnerGetNotice();
                return;
            }
        }
    }


    // ============================== 榜单 ==============================

    /**
     * 初始化全部榜单
     */
    private void initAllRankList() {
        CrossChuHanRankListModel userModel = new CrossChuHanRankListModel();
        Map<Integer, CrossChuHanRankListModel> campUserMap = new ConcurrentHashMap<>();
        Map<Integer, CrossChuHanRankListModel> campUnionMap = new ConcurrentHashMap<>();
        Map<Integer, CrossChuHanRankListModel> pveUserMap = new ConcurrentHashMap<>();

        for (CrossChuHanUserData userData : userDataMap.values()) {
            if (userData.getZhanGong() > 0) {
                CrossChuHanRank rank = new CrossChuHanRank(String.valueOf(userData.getUserId()), userData.getZhanGong(), userData.getZhanGongTime());
                // 【个人榜】
                userModel.rankChange(rank);
                // 【阵营内个人榜】
                campUserMap.putIfAbsent(userData.getCampId(), new CrossChuHanRankListModel());
                campUserMap.get(userData.getCampId()).rankChange(rank);
            }

            for (Map.Entry<Integer, Long> entry : userData.getPveZhanGongMap().entrySet()) {
                int cityType = entry.getKey();
                long pveZhanGong = entry.getValue();

                CrossChuHanRank rank = new CrossChuHanRank(String.valueOf(userData.getUserId()), pveZhanGong, userData.getPveTime(cityType));
                pveUserMap.putIfAbsent(cityType, new CrossChuHanRankListModel());
                pveUserMap.get(cityType).rankChange(rank);
            }

            compareAndSetPveFirstUser(eChuHanCityType.Type7_BeiJing.getValue(), userData);
            compareAndSetPveFirstUser(eChuHanCityType.Type8_NanYang.getValue(), userData);
        }

        for (CrossChuHanUnionJoin join : unionJoinMap.values()) {
            if (join.getCampId() != 0) {
                // 【阵营内商会榜】
                campUnionMap.putIfAbsent(join.getCampId(), new CrossChuHanRankListModel());
                CrossChuHanRank rank = getUnionZhanGongRank(join);
                campUnionMap.get(join.getCampId()).rankChange(rank);
            }
        }

        userRank = userModel;
        campUserRankMap = campUserMap;
        campUnionRankMap = campUnionMap;
        pveUserRankMap = pveUserMap;
    }

    /**
     * 获取商会战功Rank
     */
    private CrossChuHanRank getUnionZhanGongRank(CrossChuHanUnionJoin join) {
        long unionValue = 0;
        long maxTime = 0;
        for (long memberId : join.getMemberMap().keySet()) {
            CrossChuHanUserData memberData = getUserData(memberId);
            unionValue += memberData.getZhanGong();
            maxTime = Math.max(maxTime, memberData.getZhanGongTime());
        }

        CrossChuHanRank rank = new CrossChuHanRank(join.getUnionUid(), unionValue, maxTime);
        rank.setAbility(join.getTotalAbility());

        return rank;
    }

    /**
     * 阵营积分榜单
     */
    public LinkedList<CrossChuHanRank> getCampScoreRankList() {
        Map<Integer, Long> campZhangongMap = new ConcurrentHashMap<>();
        for(CrossChuHanUserData userData : userDataMap.values()){
            long oldValue = campZhangongMap.getOrDefault(userData.getCampId(),0L);
            campZhangongMap.put(userData.getCampId(),oldValue + userData.getZhanGong());
        }
        Map<Integer, Long> campScoreMap = new ConcurrentHashMap<>();
        Map<Integer, Long> updateTimeMap = new ConcurrentHashMap<>();
        for (CrossChuHanCityData city : getCrossChuHanData().getCityMap().values()) {
            int campId = city.getCampId();
            int cityId = city.getCityId();

            if (campId == 0) {
                continue;
            }

            // 统计积分
            int cityType = config.getCityConfigMap().get(cityId).getType();
            long addScore = config.getCityTypeConfigMap().get(cityType).getCampPoint();
            long oldScore = campScoreMap.getOrDefault(campId, 0L);
            campScoreMap.put(campId, oldScore + addScore);
            // 存一下时间
            if (city.getOccupyTime() > updateTimeMap.getOrDefault(campId, 0L)) {
                updateTimeMap.put(campId, city.getOccupyTime());
            }
        }

        LinkedList<CrossChuHanRank> rankList = new LinkedList<>();
        for (Map.Entry<Integer, Long> entry : campScoreMap.entrySet()) {
            int campId = entry.getKey();
            String id = String.valueOf(campId);
            CrossChuHanRank rank = new CrossChuHanRank(id, entry.getValue(), updateTimeMap.getOrDefault(entry.getKey(), System.currentTimeMillis()));
            rank.setAbility(campZhangongMap.getOrDefault(campId,0L));
            rankList.add(rank);
        }

        CrossChuHanRankListModel model = new CrossChuHanRankListModel();
        model.setRankList(rankList);

        return model.getRankList();
    }

    // ============================== 战斗 ==============================

    /**
     * 生成战场
     */
    private void createBattlefield(CrossChuHanCityData city) {
        CrossChuHanTeamArrive arrive = city.getAtkTeamList().get(0);
        CrossChuHanBattlefield battlefield = new CrossChuHanBattlefield();
        battlefield.setCreateTime(arrive.getTime());
        battlefield.setLastBattleTime(arrive.getTime());
        city.setBattlefield(battlefield);
        city.setTeamRecoveryBeginTime(0); // 城防军恢复停止计时
        teamToBattlefield(city, true);
        addTask(new CrossChuHanBattleTask(this, calcNextBattleTime(city), city.getCityId()));
    }

    /**
     * 生成分战场
     */
    private void createBattlefieldPart(CrossChuHanCityData city) {
        CrossChuHanBattlefieldTeam atkTeam = getFirstAtkTeam(city, true);
        CrossChuHanBattlefieldTeam defTeam = getFirstDefTeam(city, true);

        CrossChuHanBattlefield battlefield = city.getBattlefield();
        int partId = battlefield.getMaxPartId() + 1;
        battlefield.setMaxPartId(partId);

        CrossChuHanBattlefieldPart part = new CrossChuHanBattlefieldPart();
        part.setPartId(partId);
        part.getAtkList().add(atkTeam);
        part.getDefList().add(defTeam);

        battlefield.getPartMap().put(partId, part);
        city.setUpdateOption();
    }

    /**
     * 该城市下次战斗时间
     */
    public long calcNextBattleTime(CrossChuHanCityData city) {
        CrossChuHanBattlefield battlefield = city.getBattlefield();
        if (battlefield == null) {
            return 0;
        }

        // 上次战斗时间
        long lastBattleTime = battlefield.getLastBattleTime();

        // 第一次战斗等一分钟
        if (battlefield.getBattledNum() == 0) {
            return lastBattleTime + config.getBattlePrepareTime();
        }

        long battleCostTime = config.getBattleCostTime();
        // 有战鼓，就减为1秒（下一场生效，所以判断UseDrumTime）
        if (battlefield.getDrumTimes() > 0 && battlefield.getUseDrumTime() < lastBattleTime) {
            battleCostTime = GoodsMgr.getGoodsInfo(config.getZhanGuItem()).getParamList().get(1).intValue() * DateHelper.SECOND_MILLIONS;
        }

        return battlefield.getLastBattleTime() + battleCostTime;
    }

    /**
     * 队伍进战场
     */
    public void teamToBattlefield(CrossChuHanCityData city, boolean needSync) {

        // 记录有变化的战场，用于同步
        Set<CrossChuHanBattlefieldPart> changePartSet = new HashSet<>();

        CrossChuHanBattlefield battlefield = city.getBattlefield();

        // 【空战场先填人】
        for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
            if (part.getAtkList().isEmpty()) {
                CrossChuHanBattlefieldTeam team = getFirstAtkTeam(city, true);
                if (team != null) {
                    part.getAtkList().addLast(team);
                    changePartSet.add(part);
                }
            }
            if (part.getDefList().isEmpty()) {
                CrossChuHanBattlefieldTeam team = getFirstDefTeam(city, true);
                if (team != null) {
                    part.getDefList().addLast(team);
                    changePartSet.add(part);
                }
            }
        }

        // 【开新战场】
        for (int i = 0; i < 999; i++) {
            boolean hasAtk = getFirstAtkTeam(city, false) != null;
            boolean hasDef = getFirstDefTeam(city, false) != null;
            if (!hasAtk || !hasDef) {
                break; // 不满足两边都有队伍，无法开新战场
            }
            int curPartNum = battlefield.getPartMap().size();
            int maxPartNum = getMaxBattlefieldPartNum(city);
            if (curPartNum < maxPartNum) {
                createBattlefieldPart(city); // 没到战场最大数量，可以开新战场
                changePartSet.add(battlefield.getPartMap().get(battlefield.getMaxPartId()));
            }
        }

        Map<Integer, CrossChuHanBattlefieldPart> partMap = battlefield.getPartMap();

        // 【其余进攻方填进去】
        for (int i = 0; i < 999; i++) {
            // 没人了就不用加了
            if (getFirstAtkTeam(city, false) == null) {
                break;
            }
            // 找最小人数的战场
            CrossChuHanBattlefieldPart part = null;
            for (CrossChuHanBattlefieldPart tmp : partMap.values()) {
                if (!battlefieldPartIsFull(tmp, false) &&
                        (part == null || tmp.getAtkList().size() < part.getAtkList().size())) {
                    part = tmp;
                }
            }
            if (part != null) {
                part.getAtkList().add(getFirstAtkTeam(city, true));
                changePartSet.add(part);
            } else {
                break;
            }
        }

        // 【其余防守方填进去】
        for (int i = 0; i < 999; i++) {
            // 没人了就不用加了
            if (getFirstDefTeam(city, false) == null) {
                break;
            }
            // 找最小人数的战场
            CrossChuHanBattlefieldPart part = null;
            for (CrossChuHanBattlefieldPart tmp : partMap.values()) {
                if (!battlefieldPartIsFull(tmp, true) &&
                        (part == null || tmp.getDefList().size() < part.getDefList().size())) {
                    part = tmp;
                }
            }
            if (part != null) {
                part.getDefList().add(getFirstDefTeam(city, true));
                changePartSet.add(part);
            } else {
                break;
            }
        }

        if (needSync) {
            // 同步队伍进入战场
            for (CrossChuHanBattlefieldPart part : changePartSet) {
                syncTeamEnterBattlefield(city, battlefield, part);
            }
        }
    }

    /**
     * 战场是否填满
     */
    private boolean battlefieldPartIsFull(CrossChuHanBattlefieldPart part, boolean isDef) {
        int count = isDef ? part.getDefList().size() : part.getAtkList().size();
        int deadCount = isDef ? part.getDefDeadCount() : part.getAtkDeadCount();
        boolean win = part.getWinType() == 1 && isDef || part.getWinType() == 2 && !isDef;

        /*
         *      战场举例：
         *
         *          2   3
         *      4   5
         *
         *      firstNo = 2
         *      lineFirstNo = 1
         *      endNo = 5
         *      endNo - lineFirstNo + 1 = x    ===>    战场内队伍已经占了x个格子
         */

        // 找第一行最左边的编号
        int firstNo = deadCount + (win ? 1 : 0) + 1;
        int lineFirstNo = (firstNo + 2) / 3 * 3 - 2;

        // 队列最后一个的编号
        int endNo = deadCount + count;

        return endNo - lineFirstNo + 1 >= 6;
    }

    /**
     * 获取第一个进攻队伍
     */
    public CrossChuHanBattlefieldTeam getFirstAtkTeam(CrossChuHanCityData city, boolean remove) {
        if (city.getAtkTeamList().size() <= 0) {
            return null;
        }
        CrossChuHanTeamArrive arrive;
        if (remove) {
            arrive = city.removeAtkTeamFirst();
        } else {
            arrive = city.getAtkTeamList().getFirst();
        }
        return new CrossChuHanBattlefieldTeam(arrive);
    }

    /**
     * 获取第一个防守队伍
     */
    public CrossChuHanBattlefieldTeam getFirstDefTeam(CrossChuHanCityData city, boolean remove) {
        // 优先出战基础城防守军
        if (getSoldierTeamCount(city) > 0) {
            if (city.getTeamNum() > 0) {
                if (remove) {
                    if (!getConfig().isPveCity(city.getCityId())) {//PVE城市的守军不会减少,无限打
                        city.setTeamNum(city.getTeamNum() - 1);
                    }
                }
                // 普通城防兵
                return new CrossChuHanBattlefieldTeam(city.getTeamSoldierNum(), eTongBingSoldierType.COMMON_SOLDIER.getValue());
            } else {
                if (remove) {
                    city.setExtraTeamNum(city.getExtraTeamNum() - 1);
                }
                // 额外城防兵
                return new CrossChuHanBattlefieldTeam(city.getTeamSoldierNum(), eTongBingSoldierType.EXTRA_SOLDIER.getValue());
            }
        }

        if (city.getDefTeamList().size() <= 0) {
            return null;
        }
        CrossChuHanTeamArrive arrive;
        if (remove) {
            arrive = city.removeDefTeamFirst();
        } else {
            arrive = city.getDefTeamList().getFirst();
        }
        return new CrossChuHanBattlefieldTeam(arrive);
    }

    public void teamReturnWaitList(CrossChuHanCityData city, CrossChuHanBattlefieldPart part) {
        // 进攻方
        LinkedList<CrossChuHanBattlefieldTeam> atkList = part.getAtkList();
        while (atkList.size() > 0) {
            CrossChuHanBattlefieldTeam team = atkList.removeLast();
            if (team.getTeam() != null) {
                city.addAtkTeamFirst(team.getTeam());
            }
        }
        // 防守方
        LinkedList<CrossChuHanBattlefieldTeam> defList = part.getDefList();
        while (defList.size() > 0) {
            CrossChuHanBattlefieldTeam team = defList.removeLast();
            if (team.getTeam() != null) {
                city.addDefTeamFirst(team.getTeam());
            } else if (team.getSoldierNum() > 0) {
                if (team.getSoldierType() == eTongBingSoldierType.COMMON_SOLDIER.getValue()) {
                    city.setTeamNum(city.getTeamNum() + 1);
                } else if (team.getSoldierType() == eTongBingSoldierType.EXTRA_SOLDIER.getValue()) {
                    city.setExtraTeamNum(city.getExtraTeamNum() + 1);
                }
            }
        }
    }

    /**
     * 最大战场数
     */
    public int getMaxBattlefieldPartNum(CrossChuHanCityData city) {
        int atkNum = city.getAtkTeamList().size();
        int defNum = city.getDefTeamList().size() + getSoldierTeamCount(city);
        CrossChuHanBattlefield battlefield = city.getBattlefield();
        if (battlefield != null) {
            for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                atkNum += part.getAtkList().size();
                defNum += part.getDefList().size();
            }
        }

        int param = config.getBattlefieldPartParam();

        int num1 = atkNum / param;
        int num2 = defNum / param;

        int result = Math.min(num1, num2);

        if (result <= 0) {
            result = 1;
        }
        return result;
    }

    /**
     * 处理战斗
     */
    public CrossChuHanBattleResult handleBattle(CrossChuHanCityData city,
                                                CrossChuHanBattlefield battlefield,
                                                CrossChuHanBattlefieldPart part,
                                                long battleTime) {
        if (part.getAtkList().isEmpty() || part.getDefList().isEmpty()) {
            logger.error("battle error !!!!! cityId {} partId {}, see db log...", city.getCityId(), part.getPartId());
            return null;
        }
        CrossChuHanBattlefieldTeam atk = part.getAtkList().getFirst();
        CrossChuHanBattlefieldTeam def = part.getDefList().getFirst();

        // 阵营ID
        int atkCampId = getUserData(atk.getTeam().getUserId()).getCampId();
        int defCampId = city.getCampId();

        // 获取士兵数
        long defCount = def.getTeam() == null ? def.getSoldierNum() : getTeamByArrive(def.getTeam()).getSoldierNum();
        long atkCount = getTeamByArrive(atk.getTeam()).getSoldierNum();

        // 计算守军buff
        ChuHanCityConfig cityConfig = config.getCityConfigMap().get(city.getCityId());
        ChuHanCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap().get(cityConfig.getType());
        int defAddition = cityTypeConfig.getAddition();
        if (def != null && def.getTeam() != null) {
            defAddition += getBuffAddition(defCampId, eChuHanBuffType.Type10_DefAddition).get(0) + getUnionZhanGongLevelAddition(def.getTeam().getUserId());
        }
        if (cityConfig.getMapCamp() > 0 && cityConfig.getMapCamp() == defCampId) {
            defAddition += cityConfig.getSpecialAddition();
        }
        int atkAddition = getBuffAddition(atkCampId, eChuHanBuffType.Type9_AtkAddition).get(0) + getUnionZhanGongLevelAddition(atk.getTeam().getUserId());
        if (cityConfig.getMapCamp() > 0 && cityConfig.getMapCamp() == atkCampId) {
            atkAddition += cityConfig.getSpecialAddition();
        }

        long defCount2 = defCount * (1000 + defAddition) / 1000;
        long atkCount2 = atkCount * (1000 + atkAddition) / 1000;

        // 判断胜利方
        int winType = defCount2 == atkCount2 ? 0 : (defCount2 > atkCount2 ? 1 : 2);

        // 损兵比例
        BigDecimal defCostPercent = winType != 1 ? BigDecimal.ONE : calcBattleCostPercent(defCount2, atkCount2);
        BigDecimal atkCostPercent = winType != 2 ? BigDecimal.ONE : calcBattleCostPercent(atkCount2, defCount2);

        // 主城战斗时，守军必胜
        if (cityConfig.getType() == eChuHanCityType.Type1_MainCity.getValue()) {
            winType = 1;
            defCostPercent = BigDecimal.ZERO;
            atkCostPercent = BigDecimal.ONE;
        }

        // 计算伤亡
        long defCost = BigDecimal.valueOf(defCount).multiply(defCostPercent).setScale(0, BigDecimal.ROUND_DOWN).longValue();
        long atkCost = BigDecimal.valueOf(atkCount).multiply(atkCostPercent).setScale(0, BigDecimal.ROUND_DOWN).longValue();

        // 扣伤亡 + 加伤兵
        if (def.getTeam() != null) {
            CrossChuHanTeam defTeam = getTeamByArrive(def.getTeam());
            defTeam.setSoldierNum(defTeam.getSoldierNum() - defCost);
            long defWoundedNum = calcChangeWoundedSoldierNum(defCost);
            addBarracksWoundedSoldier(defTeam.getUserId(), defWoundedNum);
            getUserData(def.getTeam().getUserId()).setUpdateOption();
        } else {
            def.setSoldierNum(def.getSoldierNum() - defCost);
        }
        CrossChuHanTeam atkTeam = getTeamByArrive(atk.getTeam());
        atkTeam.setSoldierNum(atkTeam.getSoldierNum() - atkCost);
        long atkWoundedNum = calcChangeWoundedSoldierNum(atkCost);
        addBarracksWoundedSoldier(atkTeam.getUserId(), atkWoundedNum);
        getUserData(atk.getTeam().getUserId()).setUpdateOption();

        // 失败方回城
        if (winType != 1 && def.getTeam() != null) {
            CrossChuHanTeam team = getTeamByArrive(def.getTeam());
            team.setRouteList(Collections.singletonList(getBirthCityId(getUserData(team.getUserId()))));
            forceArriveNextCity(team, battleTime);
            handleTargetCity(team);
        }
        if (winType != 2) {
            atkTeam.setRouteList(Collections.singletonList(getBirthCityId(getUserData(atkTeam.getUserId()))));
            forceArriveNextCity(atkTeam, battleTime);
            handleTargetCity(atkTeam);
        }

        // 加战功
        long defZhanGong = calcZhanGong(defCost, defCount);
        if (def.getTeam() != null) {
            addZhanGong(def.getTeam().getUserId(), defZhanGong, battleTime);
        }
        long atkZhanGong = calcZhanGong(atkCost, atkCount);
        addZhanGong(atk.getTeam().getUserId(), atkZhanGong, battleTime);

        //加Pve战功
        addPveZhanGong(city.getCityId(), atk.getTeam().getUserId(), atkZhanGong, battleTime);

        // 连胜
        int defWinTimes = def.getTeam() == null ? 0 : battlefield.getWinTimes().getOrDefault(getTeamKey(def.getTeam()), 0);
        int atkWinTimes = battlefield.getWinTimes().getOrDefault(getTeamKey(atk.getTeam()), 0);
        if (winType == 1) {
            if (def.getTeam() != null) battlefield.getWinTimes().put(getTeamKey(def.getTeam()), ++defWinTimes);
            battlefield.getWinTimes().remove(getTeamKey(atk.getTeam()));
        } else if (winType == 2) {
            if (def.getTeam() != null) battlefield.getWinTimes().remove(getTeamKey(def.getTeam()));
            if (def.getTeam() != null)
                battlefield.getWinTimes().put(getTeamKey(atk.getTeam()), ++atkWinTimes); // 打城防军不算连胜
        } else {
            if (def.getTeam() != null) battlefield.getWinTimes().remove(getTeamKey(def.getTeam()));
            battlefield.getWinTimes().remove(getTeamKey(atk.getTeam()));
        }
        // 连胜公告
        checkContinueWinReport(city.getCityId(), winType, battleTime,
                def, defWinTimes,
                atk, atkWinTimes);

        // 战场排行
        if (def.getTeam() != null) {
            battlefield.rankChange(def.getTeam().getUserId(), defZhanGong, atkCost);
        }
        battlefield.rankChange(atkTeam.getUserId(), atkZhanGong, defCost);

        // 记录战斗结果
        if (winType != 1) {
            part.getDefList().removeFirst();
            part.setDefDeadCount(part.getDefDeadCount() + 1);
        }
        if (winType != 2) {
            part.getAtkList().removeFirst();
            part.setAtkDeadCount(part.getAtkDeadCount() + 1);
        }
        part.setWinType(winType);

        city.setUpdateOption();

        // 记录个战报
        CrossChuHanUserWarReport userReport = new CrossChuHanUserWarReport(getConfig().getActivityInfo().getActivityId(), groupId, reportId.getAndIncrement());
        if (def.getTeam() == null) {
            userReport.setUserIdDef(0);
            userReport.setDefPatronsId(0);
        } else {
            CrossChuHanTeam defTeam = getTeamByArrive(def.getTeam());
            userReport.setUserIdDef(defTeam.getUserId());
            userReport.setDefPatronsId(defTeam.getPatronsList().isEmpty() ? 0 : defTeam.getPatronsList().get(0));
        }
        userReport.setUserIdAct(atkTeam.getUserId());
        userReport.setActPatronsId(atkTeam.getPatronsList().isEmpty() ? 0 : atkTeam.getPatronsList().get(0));
        userReport.setDefSoldierNum(defCount);
        userReport.setActSoldierNum(atkCount);
        userReport.setDefAddition(defAddition);
        userReport.setActAddition(atkAddition);
        userReport.setDefZhanGong(defZhanGong);
        userReport.setActZhanGong(atkZhanGong);
        userReport.setDefDieSoldierNum(defCost);
        userReport.setActDieSoldierNum(atkCost);
        userReport.setWinUserId(winType);
        userReport.setTime(battleTime);
        userReport.setCityId(city.getCityId());
        addUserWarReport(userReport);

        CrossAutoLogMgr.add(new LogChuHanUserWar(
                userReport.getActivityId(),
                userReport.getGroupId(),
                userReport.getNo(),
                userReport.getCityId(),
                userReport.getUserIdDef(),
                userReport.getUserIdAct(),
                atkCampId,
                defCampId,
                userReport.getDefPatronsId(),
                userReport.getActPatronsId(),
                userReport.getDefSoldierNum(),
                userReport.getActSoldierNum(),
                userReport.getDefAddition(),
                userReport.getActAddition(),
                userReport.getDefZhanGong(),
                userReport.getActZhanGong(),
                userReport.getDefDieSoldierNum(),
                userReport.getActDieSoldierNum(),
                winType
                ));

        // 返回结果
        return new CrossChuHanBattleResult(
                part, battlefield, city,
                winType,
                def, defCost, defZhanGong,
                atk, atkCost, atkZhanGong,
                winType == 1 ? defWinTimes : winType == 2 ? atkWinTimes : 0
        );
    }

    private int getUnionZhanGongLevelAddition(long userId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return 0;
        }

        long unionZhanGong = getUnionZhanGong(userData.getUnionUid());
        int level = getUnionZhanGongLevel(unionZhanGong);
        return getConfig().getUnionLevelAddition(level);
    }

    /**
     * 处理战略目标操作
     */
    private void handleTargetCity(CrossChuHanTeam team) {
        if (team.getTargetCityId() != 0) {
            eTongBingStage stage = this.config.getTongBingStage();
            if (stage != eTongBingStage.Stage1Time) {
                team.setTargetCityId(0);
                addNeedSyncTeam(team);
                return;
            }

            if (!checkCityCanSetTarget(getUserData(team.getUserId()).getCampId(), team.getTargetCityId())) {
                team.setTargetCityId(0);
                addNeedSyncTeam(team);
                return;
            }

            if (!moveToTargetCity(team)) {
                // 如果兵力不足或者无法前往城池，取消战略目标
//                team.setTargetCityId(0);
                // 队伍同步
//                syncTeamToSelf(team.getUserId(), Collections.singletonList(team));
            }
        }
    }

    /**
     * 补满兵自动前往战略目标
     */
    private boolean moveToTargetCity(CrossChuHanTeam team) {
        CrossChuHanUserData userData = getUserData(team.getUserId());
        if (!isAble(team.getUserId())) {
            return false;
        }

        if (getTeamStatus(team) == eTongBingTeamStatus.Free) {
            // 判断预备兵是否足够
            long needNum = team.getMaxSoldier() - team.getSoldierNum();
            if (needNum > 0) {
                if (needNum <= userData.getBarracks().getReadyNum()) {
                    // 预备兵扣除
                    userData.getBarracks().setReadyNum(userData.getBarracks().getReadyNum() - needNum);
                    // 补满兵力
                    team.setSoldierNum(team.getSoldierNum() + needNum);
                    userData.setUpdateOption();

                    addNeedSyncTeam(team);
                    addNeedSyncBarracksUser(team.getUserId());
                } else {//不够补充就停下
                    team.setTargetCityId(0);
                    addNeedSyncTeam(team);
                    return false;
                }
            }
        } else {
            return false;
        }

        if (team.getCityId() == team.getTargetCityId()) {
            return false;
        }

        // 前往战略目标
        return moveToCity(team, team.getTargetCityId()) == 0;
    }

    /**
     * 检查连胜公告
     */
    private void checkContinueWinReport(int cityId, int winType, long time,
                                        CrossChuHanBattlefieldTeam defTeam, int defWinTimes,
                                        CrossChuHanBattlefieldTeam atkTeam, int atkWinTimes) {
        int num = config.getContinueWinReportTimes();

        // 拿昵称
        String defNickname;
        if (defTeam.getTeam() != null) {
            UserBaseInfo defInfo = CrossUserMgr.getUserBaseInfo(defTeam.getTeam().getUserId());
            defNickname = defInfo == null ? "" : defInfo.getNickName();
        } else {
            defNickname = config.getCityDefTeamNameKey();
        }
        UserBaseInfo atkInfo = CrossUserMgr.getUserBaseInfo(atkTeam.getTeam().getUserId());
        String atkNickname = atkInfo == null ? "" : atkInfo.getNickName();

        // 看连胜
        if (winType == 1 && defWinTimes >= num) {
            addSystemChat(eMessageType.ChuHanBattlefieldWinChat, cityId,
                    1 + GameConfig.TALK_SEPARATOR + defNickname + GameConfig.TALK_SEPARATOR + defWinTimes + GameConfig.TALK_SEPARATOR + atkNickname + GameConfig.TALK_SEPARATOR + atkWinTimes
                    , time);
        }
        if (winType == 2 && atkWinTimes >= num) {
            addSystemChat(eMessageType.ChuHanBattlefieldWinChat, cityId,
                    1 + GameConfig.TALK_SEPARATOR + atkNickname + GameConfig.TALK_SEPARATOR + atkWinTimes + GameConfig.TALK_SEPARATOR + defNickname + GameConfig.TALK_SEPARATOR + defWinTimes
                    , time);
        }
        // 看终结连胜
        if (winType != 1 && defWinTimes >= num) {
            addSystemChat(eMessageType.ChuHanBattlefieldWinChat, cityId,
                    2 + GameConfig.TALK_SEPARATOR + atkNickname + GameConfig.TALK_SEPARATOR + atkWinTimes + GameConfig.TALK_SEPARATOR + defNickname + GameConfig.TALK_SEPARATOR + defWinTimes
                    , time);
        }
        if (winType != 2 && atkWinTimes >= num) {
            addSystemChat(eMessageType.ChuHanBattlefieldWinChat, cityId,
                    2 + GameConfig.TALK_SEPARATOR + defNickname + GameConfig.TALK_SEPARATOR + defWinTimes + GameConfig.TALK_SEPARATOR + atkNickname + GameConfig.TALK_SEPARATOR + atkWinTimes
                    , time);
        }
    }

    /**
     * 加战功值
     */
    private void addZhanGong(long userId, long zhanGong, long battleTime) {

        // 领奖期不能加战功
        if (battleTime >= config.getActivityInfo().getEndTime() * 1000) {
            return;
        }

        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        String userRankId = String.valueOf(userId);

        // （之前是否发光）
        int lightBeforeRank = Integer.MAX_VALUE;
        if (userData.getZhanGong() >= config.getLightNeedZhanGong()) {
            int rankNum = 0;
            for (CrossChuHanRank tmp : userRank.getRankList()) {
                if (++rankNum > config.getLightRankNum()) {
                    break;
                }
                if (Objects.equals(tmp.getId(), userRankId)) {
                    lightBeforeRank = rankNum;
                    break;
                }
            }
        }

        // 加战功
        userData.setZhanGong(userData.getZhanGong() + zhanGong);
        userData.setZhanGongTime(battleTime);

        // 个人榜
        CrossChuHanRank rank = new CrossChuHanRank(String.valueOf(userId), userData.getZhanGong(), System.currentTimeMillis());
        userRank.rankChange(rank);

        // 阵营内个人榜
        campUserRankMap.putIfAbsent(userData.getCampId(), new CrossChuHanRankListModel());
        campUserRankMap.get(userData.getCampId()).rankChange(rank);

        // 阵营内商会榜
        CrossChuHanUnionJoin join = unionJoinMap.get(userData.getUnionUid());
        if (join != null) {
            long unionValue = 0;
            for (long memberId : join.getMemberMap().keySet()) {
                CrossChuHanUserData memberData = getUserData(memberId);
                unionValue += memberData.getZhanGong();
            }

            CrossChuHanRank unionRank = new CrossChuHanRank(join.getUnionUid(), unionValue, System.currentTimeMillis());
            unionRank.setAbility(join.getTotalAbility());

            campUnionRankMap.putIfAbsent(userData.getCampId(), new CrossChuHanRankListModel());
            campUnionRankMap.get(userData.getCampId()).rankChange(unionRank);
        }

        // 之后是否发光
        int lightAfterRank = Integer.MAX_VALUE;
        if (userData.getZhanGong() >= config.getLightNeedZhanGong()) {
            int rankNum = 0;
            for (CrossChuHanRank tmp : userRank.getRankList()) {
                if (++rankNum > config.getLightRankNum()) {
                    break;
                }
                if (Objects.equals(tmp.getId(), userRankId)) {
                    lightAfterRank = rankNum;
                    break;
                }
            }
        }

        // 发光
        if (lightAfterRank <= config.getLightRankNum()) {
            if (lightBeforeRank > config.getLightRankNum()) { // 新发光
                syncLight();
            } else if (lightAfterRank != lightBeforeRank) { // 旧发光，但光变了
                syncLight();
            }
        }

        // 通知游戏服更新condition成就
        notifyConditionChange(userId, eGamePlayerEventType.ChuHanZhanGong, userData.getZhanGong());
        notifyConditionChange(userId, eGamePlayerEventType.ChuHanUnionZhanGong, getUnionZhanGong(userData.getUnionUid()));
        // 同步用户数据
        CrossChuHanUnionJoin unionJoin = getUnionJoin(userData.getUnionUid());
        if (unionJoin != null) {
            for (CrossChuHanUnionJoinMember joinMember : unionJoin.getMemberMap().values()) {
                addNeedSyncBaseUser(joinMember.getUserId());
            }
        }
    }

    private void addPveZhanGong(int cityId, long userId, long zhanGong, long battleTime) {
        ChuHanCityConfig cityConfig = getConfig().getCityConfigMap().get(cityId);
        if (cityConfig == null) {
            return;
        }

        int cityType = cityConfig.getType();
        if (!getConfig().isPveCityByCityType(cityConfig.getType())) {
            return;
        }

        // 领奖期不能加战功
        if (battleTime >= config.getActivityInfo().getEndTime() * 1000) {
            return;
        }

        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        String userRankId = String.valueOf(userId);


        if (!config.isPveCitySettleTime(cityType)){
            // 加战功
            userData.addPveZhanGong(cityConfig.getType(), zhanGong, battleTime);
            //加阵营总PVE战功
            CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
            campData.addPveZhanGong(cityConfig.getType(), zhanGong, battleTime);
            //更新下第一名
            compareAndSetPveFirstUser(cityType, userData);
            // 榜单更新
            CrossChuHanRank rank = new CrossChuHanRank(String.valueOf(userId), userData.getPveZhanGong(cityType), userData.getPveTime(cityType));

            // 阵营内个人榜
            pveUserRankMap.putIfAbsent(cityType, new CrossChuHanRankListModel());
            pveUserRankMap.get(cityType).rankChange(rank);
        }
    }

    private void compareAndSetPveFirstUser(int cityType, CrossChuHanUserData userData) {
        if (!getConfig().isPveCityByCityType(cityType)) {
            return;
        }
        if (userData.getPveZhanGong(cityType) == 0) {
            return;
        }

        //个人第一
        {
            CrossChuHanUserData oldFirstUser = pveFirstUserDataMap.get(cityType);
            if (oldFirstUser == null) {
                pveFirstUserDataMap.put(cityType, userData);
            } else if (oldFirstUser.getUserId() != userData.getUserId()) {
                long oldFirstZhanGong = oldFirstUser.getPveZhanGong(cityType);
                long oldPveTime = oldFirstUser.getPveTime(cityType);

                long compareZhanGong = userData.getPveZhanGong(cityType);
                long comparePveTime = userData.getPveTime(cityType);

                if (compareZhanGong > oldFirstZhanGong || (compareZhanGong == oldFirstZhanGong && comparePveTime < oldPveTime)) {
                    pveFirstUserDataMap.put(cityType, userData);
                }
            }
        }


        //阵营内个人第一
        {
            Map<Integer, CrossChuHanUserData> firstMap = pveCampFirstUserDataMap.get(cityType);
            if (firstMap == null) {
                firstMap = new ConcurrentHashMap<>();
                pveCampFirstUserDataMap.put(cityType, firstMap);
            }
            CrossChuHanUserData oldFirstUser = firstMap.get(userData.getCampId());
            if (oldFirstUser == null) {
                firstMap.put(userData.getCampId(), userData);
            } else if (oldFirstUser.getUserId() != userData.getUserId()) {
                long oldFirstZhanGong = oldFirstUser.getPveZhanGong(cityType);
                long oldPveTime = oldFirstUser.getPveTime(cityType);
                long compareZhanGong = userData.getPveZhanGong(cityType);
                long comparePveTime = userData.getPveTime(cityType);

                if (compareZhanGong > oldFirstZhanGong || (compareZhanGong == oldFirstZhanGong && comparePveTime < oldPveTime)) {
                    firstMap.put(userData.getCampId(), userData);
                }
            }
        }
    }

    /**
     * 计算转换伤兵数量
     */
    private long calcChangeWoundedSoldierNum(long deadNum) {
        return deadNum * config.getWoundedSoldierPercent() / 1000;
    }

    /**
     * 计算战功
     */
    private long calcZhanGong(long soldierCostNum, long mySoldierNum) {
        BigDecimal param1 = BigDecimal.valueOf(config.getZhanGongParam().get(0));
        BigDecimal param2 = BigDecimal.valueOf(config.getZhanGongParam().get(1));
        BigDecimal param3 = BigDecimal.valueOf(config.getZhanGongParam().get(2));
        BigDecimal param4 = BigDecimal.valueOf(config.getZhanGongParam().get(3));
        BigDecimal costNum = BigDecimal.valueOf(soldierCostNum);
        BigDecimal one = BigDecimal.ONE;
        BigDecimal myNum = BigDecimal.valueOf(mySoldierNum);

        BigDecimal part2 = myNum.divide(param4, 8, BigDecimal.ROUND_DOWN); // 我方兵数/参数4
        BigDecimal part3 = param1.divide(param2, 8, BigDecimal.ROUND_DOWN); // 参数1/参数2

        BigDecimal value = one
                .add(part2.multiply(part3))
                .multiply(costNum)
                .multiply(param3);

        return value.setScale(0, BigDecimal.ROUND_DOWN).longValue();
    }

    /**
     * 计算损兵比例
     */
    private BigDecimal calcBattleCostPercent(long myCount, long enemyCount) {
        // 容错
        myCount = myCount <= 0 ? 1 : myCount;
        enemyCount = enemyCount <= 0 ? 1 : enemyCount;

        BigDecimal param1 = BigDecimal.valueOf(config.getBattleParam().get(0));
        BigDecimal param2 = BigDecimal.valueOf(config.getBattleParam().get(1));
        BigDecimal param3 = BigDecimal.valueOf(config.getBattleParam().get(2));
        BigDecimal param4 = BigDecimal.valueOf(config.getBattleParam().get(3));
        BigDecimal param5 = BigDecimal.valueOf(config.getBattleParam().get(4));
        BigDecimal param6 = BigDecimal.valueOf(config.getBattleParam().get(5));
        BigDecimal param7 = BigDecimal.valueOf(config.getBattleParam().get(6));

        // 敌方兵数/我方兵数
        BigDecimal partSoldier = BigDecimal.valueOf(enemyCount).divide(BigDecimal.valueOf(myCount), 8, BigDecimal.ROUND_DOWN);
        // 参数1/参数2
        BigDecimal part12 = param1.divide(param2, 8, BigDecimal.ROUND_DOWN);
        // 参数3/参数4
        BigDecimal part34 = param3.divide(param4, 8, BigDecimal.ROUND_DOWN);
        // 参数5/参数6
        BigDecimal part56 = param5.divide(param6, 8, BigDecimal.ROUND_DOWN);

        BigDecimal partSoldier12 = BigDecimal.valueOf(Math.pow(partSoldier.doubleValue(), part12.doubleValue())).setScale(8, BigDecimal.ROUND_DOWN);

        BigDecimal result = partSoldier12.multiply(part34)
                .add(partSoldier.multiply(part56))
                .add(param7)
                .multiply(partSoldier)
                .setScale(8, BigDecimal.ROUND_DOWN);

        // 不能超过1
        if (result.compareTo(BigDecimal.ONE) > 0) {
            result = BigDecimal.ONE;
        }

        return result;
    }

    /**
     * 记录城池战报
     */
    public CrossChuHanCityWarReport recordBattleReport(CrossChuHanCityData city, long time) {

        // 城池战报数据准备
        int defCampId = city.getCampId();
        CrossChuHanBattlefield battlefield = city.getBattlefield();
        LinkedList<CrossChuHanBattlefieldRank> rankList = battlefield.getBattlefieldRankNewList();
        LinkedList<CrossChuHanBattlefieldRank> atkRank = new LinkedList<>();
        LinkedList<CrossChuHanBattlefieldRank> defRank = new LinkedList<>();
        Set<Integer> atkCampSet = new HashSet<>();
        for (CrossChuHanBattlefieldRank rank : rankList) {
            int campId = getUserData(rank.getUserId()).getCampId();
            if (defCampId == campId) {
                if (defRank.size() < 3) {
                    defRank.add(rank);
                }
            } else {
                if (atkRank.size() < 3) {
                    atkRank.add(rank);
                }
                atkCampSet.add(campId);
            }
        }

        boolean defWin = city.getAtkTeamList().isEmpty();
        int winCampId = defCampId;
        if (!defWin) {
            CrossChuHanTeamArrive first = city.getAtkTeamList().getFirst();
            winCampId = getUserData(first.getUserId()).getCampId();
        }

        // 加城池战报
        CrossChuHanCityWarReport report = new CrossChuHanCityWarReport();
        report.setActivityId(config.getActivityInfo().getActivityId());
        report.setGroupId(groupId);
        report.setAtkCampId(new ArrayList<>(atkCampSet));
        report.setDefCampId(defCampId);
        report.setType(defWin ? 2 : 1);
        report.setCityId(city.getCityId());
        report.setTime(battlefield.getLastBattleTime());
        report.setAtkRankList(atkRank);
        report.setDefRankList(defRank);
        report.setWinCampId(winCampId);
        addCityWarReport(report);

        return report;
    }

    /**
     * 处理城池战斗结束
     */
    public void handleBattleFinish(CrossChuHanCityData city, long time) {

        int defCampId = city.getCampId();

        CrossChuHanBattlefield battlefield = city.getBattlefield();

        ChuHanCityConfig cityConfig = config.getCityConfigMap().get(city.getCityId());
        ChuHanCityTypeConfig typeConfig = config.getCityTypeConfigMap().get(cityConfig.getType());

        boolean defWin = city.getAtkTeamList().isEmpty();
        if (!defWin) {
            // 占领了
            CrossChuHanTeamArrive first = city.getAtkTeamList().getFirst();
            int campId = getUserData(first.getUserId()).getCampId();

            // 同阵营队伍抽出来
            LinkedList<CrossChuHanTeamArrive> atkList = new LinkedList<>();
            LinkedList<CrossChuHanTeamArrive> defList = new LinkedList<>();
            for (CrossChuHanTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
                if (campId == getUserData(arrive.getUserId()).getCampId()) {
                    defList.add(arrive);
                } else {
                    atkList.add(arrive);
                }
            }
            atkList.sort(Comparator.comparing(CrossChuHanTeamArrive::getTime));
            defList.sort(Comparator.comparing(CrossChuHanTeamArrive::getTime));

            int oldCityCampId = city.getCampId();
            // BUFF换阵营前先结算一下
            beforeCityChangeBuff(city.getCityId(), oldCityCampId, campId, time);
            //领取一下城池收货
            makeAllCityCollect(city.getCampId(), time);

            // 修改城池数据
            city.setCampId(campId);
            city.setCollectionTime(time);
            city.setOccupyTime(time);
            city.setDefTeamList(defList);
            city.setAtkTeamList(atkList);

            campOccupyCity(city, campId);

            //检查下把出生点设置在这里的玩家,被别的阵营占领了,要改成根据地
            checkUserBirthCity(city);

            //更新阵营BUFF
            if(getConfig().isCityHaveBuff(city.getCityId())) {
                if (oldCityCampId > 0) {
                    refreshCampBuff(oldCityCampId);
                }
                refreshCampBuff(campId);

                isNeedSyncBuff.set(true);
            }

            // 再计算buff
            afterCityChangeBuff(city.getCityId(), oldCityCampId, campId);


            // 如果有进攻方，准继续战斗
            if (city.getAtkTeamList().size() > 0) {
                addTask(new CrossChuHanBattleTask(this, calcNextBattleTime(city), city.getCityId()));
            }
            // 被占领后开始恢复额外守军
            city.setLastRecoveryExtraTeamTime(time);
            addTask(new CrossChuHanRecoveryExtraTeamTask(this, getNextRecoveryExtraTeamTime(city), city.getCityId()));
        }

        // 城池守军恢复时间
        city.setTeamRecoveryBeginTime(time);
        addTask(new CrossChuHanCityTeamRecoveryTask(this, config.getCityTeamRecoveryTime() + city.getTeamRecoveryBeginTime(), city.getCityId()));

        // 加聊天
        addSystemChat(eMessageType.ChuHanPublicBattleChat, config.getActivityInfo().getActivityId(),
                (!defWin ? 1 : 2) + GameConfig.TALK_SEPARATOR + city.getCityId() + GameConfig.TALK_SEPARATOR + city.getCampId() + GameConfig.TALK_SEPARATOR + city.getBattledNum() + GameConfig.TALK_SEPARATOR + defCampId,
                time);

        // 同步顶部横幅
        syncTopMsg(city.getCampId(), city.getCityId(), defWin ? 2 : 1, battlefield, defCampId);
    }

    private void beforeCityChangeBuff(int cityId, int oldCampId, int newCampId, long time) {
        ChuHanCityConfig cityConfig = getConfig().getCityConfigMap().get(cityId);
        if (cityConfig == null) {
            return;
        }
        if (cityConfig.getBuffId() > 0) {
            int buffId = cityConfig.getBuffId();
            if (oldCampId > 0 ) {
                beforeEndBuff(buffId, oldCampId, time);
            }
            beforeBeginBuff(buffId, newCampId, time);
        }

        ChuHanCityTypeConfig typeConfig = getConfig().getCityTypeConfigMap().get(cityConfig.getType());
        if (typeConfig != null && typeConfig.getBuffEffect() > 0) {
            int buffId = typeConfig.getBuffEffect();
            if (oldCampId > 0 ) {
                beforeEndBuff(buffId, oldCampId, time);
            }
            beforeBeginBuff(buffId, newCampId, time);
        }
    }

    private void afterCityChangeBuff(int cityId, int oldCampId, int newCampId) {
        ChuHanCityConfig cityConfig = getConfig().getCityConfigMap().get(cityId);
        if (cityConfig == null) {
            return;
        }
        if (cityConfig.getBuffId() > 0) {
            int buffId = cityConfig.getBuffId();
            if (oldCampId > 0 ) {
                afterEndBuff(buffId, oldCampId);
            }
            afterBeginBuff(buffId, newCampId);
        }

        ChuHanCityTypeConfig typeConfig = getConfig().getCityTypeConfigMap().get(cityConfig.getType());
        if (typeConfig != null && typeConfig.getBuffEffect() > 0) {
            int buffId = typeConfig.getBuffEffect();
            if (oldCampId > 0 ) {
                afterEndBuff(buffId, oldCampId);
            }
            afterBeginBuff(buffId, newCampId);
        }
    }

    private void campOccupyCity(CrossChuHanCityData city, int campId) {
        CrossChuHanCampData campData = campDataMap.get(campId);
        if (campData != null) {
            int day = ActivityHelper.getWhichDayAfterBeginTime(getConfig().getActivityInfo());
            campData.addDayOccupyCity(day, city.getCityId());
        }

        int day = ActivityHelper.getWhichDayAfterBeginTime(getConfig().getActivityInfo());
        if (crossChuHanData.getOrderCityMap().containsKey(day)){
            CrossChuHanOrderCity orderCity = crossChuHanData.getOrderCityMap().get(day);
            for (List<Integer> cityIds : orderCity.getOrderCityMap().values()) {
                if (cityIds.contains(city.getCityId())) {
                    isNeedSyncOrderCityTask.set(true);
                    break;
                }
            }
        }


    }

    private void checkUserBirthCity(CrossChuHanCityData city) {
        for (CrossChuHanUserData userData : userDataMap.values()) {
            if (userData.getBirthCityId() != city.getCityId()) {
                continue;
            }
            if (userData.getCampId() != city.getCampId()) {
                userData.setBirthCityId(0);
                userData.setFlitTime(0);

                syncBaseDataMsg(userData);
            }
        }
    }

    /**
     * 未战斗直接攻取城池
     */
    public void handleNoBattleOccupy(CrossChuHanCityData city, long time) {

        ChuHanCityConfig cityConfig = config.getCityConfigMap().get(city.getCityId());
        ChuHanCityTypeConfig typeConfig = config.getCityTypeConfigMap().get(cityConfig.getType());

        int atkCampId = getUserData(city.getAtkTeamList().getFirst().getUserId()).getCampId();
        int defCampId = city.getCampId();
        int winCampId = atkCampId;
        boolean defWin = false;

        // 【加城池战报】recordBattleReport

        CrossChuHanCityWarReport report = new CrossChuHanCityWarReport();
        report.setActivityId(config.getActivityInfo().getActivityId());
        report.setGroupId(groupId);
        report.setAtkCampId(Collections.singletonList(atkCampId));
        report.setDefCampId(defCampId);
        report.setType(defWin ? 2 : 1);
        report.setCityId(city.getCityId());
        report.setTime(time);
        report.setAtkRankList(new LinkedList<>());
        report.setDefRankList(new LinkedList<>());
        report.setWinCampId(winCampId);
        addCityWarReport(report);

        // 【处理战斗结束】handleBattleFinish

        // 同阵营队伍抽出来
        LinkedList<CrossChuHanTeamArrive> atkList = new LinkedList<>();
        LinkedList<CrossChuHanTeamArrive> defList = new LinkedList<>();
        for (CrossChuHanTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
            if (winCampId == getUserData(arrive.getUserId()).getCampId()) {
                defList.add(arrive);
            } else {
                atkList.add(arrive);
            }
        }
        atkList.sort(Comparator.comparing(CrossChuHanTeamArrive::getTime));
        defList.sort(Comparator.comparing(CrossChuHanTeamArrive::getTime));

        // 先计算buff
        int oldCityCampId = city.getCampId();
        // BUFF换阵营前先结算一下
        beforeCityChangeBuff(city.getCityId(), oldCityCampId, winCampId, time);

        //领取一下城池收货
        makeAllCityCollect(city.getCampId(), time);

        // 修改城池数据
        city.setCampId(winCampId);
        city.setCollectionTime(time);
        city.setOccupyTime(time);
        city.setDefTeamList(defList);
        city.setAtkTeamList(atkList);

        campOccupyCity(city, winCampId);

        //检查下把出生点设置在这里的玩家,被别的阵营占领了,要改成根据地
        checkUserBirthCity(city);

        //更新阵营BUFF
        if(getConfig().isCityHaveBuff(city.getCityId())) {
            if (oldCityCampId > 0) {
                refreshCampBuff(oldCityCampId);
            }
            refreshCampBuff(winCampId);

            isNeedSyncBuff.set(true);
        }

        // 再计算buff
        afterCityChangeBuff(city.getCityId(), oldCityCampId, winCampId);

        // 如果有进攻方，准继续战斗
        if (city.getAtkTeamList().size() > 0) {
            addTask(new CrossChuHanBattleTask(this, calcNextBattleTime(city), city.getCityId()));
        }

        // 城池守军恢复时间
        city.setTeamRecoveryBeginTime(time);

        addTask(new CrossChuHanCityTeamRecoveryTask(this, config.getCityTeamRecoveryTime() + city.getTeamRecoveryBeginTime(), city.getCityId()));

        // 被占领后开始恢复额外守军
        city.setLastRecoveryExtraTeamTime(time);
        addTask(new CrossChuHanRecoveryExtraTeamTask(this, getNextRecoveryExtraTeamTime(city), city.getCityId()));

        // 加聊天
        addSystemChat(eMessageType.ChuHanPublicBattleChat, config.getActivityInfo().getActivityId(),
                (!defWin ? 1 : 2) + GameConfig.TALK_SEPARATOR + city.getCityId() + GameConfig.TALK_SEPARATOR + city.getCampId() + GameConfig.TALK_SEPARATOR + 0 + GameConfig.TALK_SEPARATOR + defCampId,
                time);

        // 同步顶部横幅
        syncTopMsg(city.getCampId(), city.getCityId(), defWin ? 2 : 1, null, defCampId);
    }

    /**
     * 移除空战场
     *
     * @return 清空战场的ID
     */
    public List<Integer> removeEmptyBattlefield(CrossChuHanCityData city) {
        List<Integer> logRemove = new ArrayList<>();
        CrossChuHanBattlefield battlefield = city.getBattlefield();
        Map<Integer, CrossChuHanBattlefieldPart> partMap = battlefield.getPartMap();
        for (Map.Entry<Integer, CrossChuHanBattlefieldPart> entry : partMap.entrySet()) {
            CrossChuHanBattlefieldPart part = entry.getValue();
            LinkedList<CrossChuHanBattlefieldTeam> atkList = part.getAtkList();
            LinkedList<CrossChuHanBattlefieldTeam> defList = part.getDefList();
            if (atkList.isEmpty() || defList.isEmpty()) {
                // 队伍回到排队里
                teamReturnWaitList(city, part);
                // 移除空战场
                partMap.remove(entry.getKey());
                // 记录ID
                logRemove.add(part.getPartId());
            }
        }
        return logRemove;
    }

    private int getBattleField(long userId, ChuHanProto.ChuHanGetBattlefieldReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();
        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
        if (city == null) {
            return GameErrorCode.E_CHUHAN_PARAM_ERR;
        }
        CrossChuHanBattlefield battlefield = city.getBattlefield();
        if (battlefield == null) {
            return GameErrorCode.E_CHUHAN_NO_BATTLE;
        }
        CrossChuHanBattlefieldPart part = null;
        if (reqMsg.hasPartId()) {
            part = battlefield.getPartMap().get(reqMsg.getPartId());
        } else {
            for (CrossChuHanBattlefieldPart tmp : battlefield.getPartMap().values()) {
                part = tmp;
                break;
            }
        }
        if (part == null) {
            return GameErrorCode.E_CHUHAN_NO_BATTLE;
        }

        ChuHanProto.ChuHanGetBattlefieldRespMsg.Builder builder = ChuHanProto.ChuHanGetBattlefieldRespMsg.newBuilder();
        builder.setRet(0);
        builder.setBattlefield(parseChuHanBattlefieldMsg(part, battlefield, city));

        // 进入场景
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        if (userBaseInfo != null) {
            ScenePlayerInfo scenePlayerInfo = new ScenePlayerInfo(userBaseInfo.getServerId(), userId, userBaseInfo.getNickName(), userBaseInfo.getDecoration());
            UserScene userScene = new UserScene(
                    config.getActivityInfo().getActivityId(),
                    eSceneType.TongBingBattlefield.getValue(),
                    getBattlefieldSceneKey(cityId, part.getPartId()),
                    userId,
                    scenePlayerInfo,
                    System.currentTimeMillis());
            CrossSceneMgr.enterScene(userScene, false);
        }
        sendPacket(Protocol.S_CHUHAN_GET_BATTLE_FIELD % 20000, builder, userId);
        return 0;
    }

    /**
     * 使用战鼓
     */
    public int useWarDrum(long userId, ChuHanProto.ChuHanUseWarDrumReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();

        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
        if (city == null) {
            return GameErrorCode.E_CHUHAN_CITY_NO_EXIST;
        }

        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            return GameErrorCode.E_CHUHAN_NO_ABLE;
        }

        synchronized (lock) {

            // 是否战斗中
            CrossChuHanBattlefield battlefield = city.getBattlefield();
            if (battlefield == null) {
                return GameErrorCode.E_CHUHAN_NO_BATTLE;
            }

            // 拿道具效果
            GoodsInfo goodsInfo = GoodsMgr.getGoodsInfo(config.getZhanGuItem());
            if (goodsInfo == null) {
                return GameErrorCode.E_BAG_ITEM_NO_EXIST;
            }
            int effectTimes = goodsInfo.getParamList().get(0).intValue();

            // 有基础城防军无法使用战鼓
            int baseDefTeamNum = getDefTeamNum(city, battlefield, eTongBingSoldierType.COMMON_SOLDIER.getValue());
            if (!config.isPveCity(cityId)) {//PVE城市不限制
                if (baseDefTeamNum > 0) {
                    return GameErrorCode.E_CHUHAN_HAS_DEF_TEAM;
                }
            }

            if (baseDefTeamNum == 0 && getDefTeamNum(city, battlefield, eTongBingSoldierType.EXTRA_SOLDIER.getValue()) == 0) {
                for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                    for (CrossChuHanBattlefieldTeam team : new LinkedList<>(part.getDefList())) {
                        if (team.getTeam() == null) {
                            return GameErrorCode.E_CHUHAN_HAS_DEF_TEAM;
                        }
                    }
                }
            }

            // 看看有没有自己的阵营在里面
            int myCampId = userData.getCampId();
            boolean hasMyCamp = false;
            if (city.getCampId() == myCampId) {
                hasMyCamp = true;
            }
            if (!hasMyCamp) {
                for (CrossChuHanTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
                    if (getUserData(arrive.getUserId()).getCampId() == myCampId) {
                        hasMyCamp = true;
                        break;
                    }
                }
            }
            if (!hasMyCamp) {
                for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                    for (CrossChuHanBattlefieldTeam tempTeam : new LinkedList<>(part.getAtkList())) {
                        if (myCampId == getUserData(tempTeam.getTeam().getUserId()).getCampId()) {
                            hasMyCamp = true;
                            break;
                        }
                    }
                }
            }
            if (!hasMyCamp) {
                return GameErrorCode.E_CHUHAN_NO_MY_CAMP_TEAM;
            }

            // 【统兵演武取消加速限制】
//            // 当战场的加速场次＞较高方部队数/战场数的时候,无需加速
//            int atkTeamCnt = 0;
//            int defTeamCnt = 0;
//            for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
//                atkTeamCnt += part.getAtkList().size();
//                defTeamCnt += part.getDefList().size();
//            }
//            if (battlefield.getPartMap().size() != 0 && battlefield.getDrumTimes() > (Math.max(atkTeamCnt, defTeamCnt) / battlefield.getPartMap().size())) {
//                return GameErrorCode.E_CHUHAN_USE_DRUM_LIMIT;
//            }

            // 战鼓生效
            if (battlefield.getDrumTimes() <= 0) {
                battlefield.setUseDrumTime(System.currentTimeMillis());
            }
            battlefield.setDrumTimes(battlefield.getDrumTimes() + effectTimes);
            city.setUpdateOption();

            // 同步所有人
            for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                syncUseWarDrum(city, battlefield, part);
            }
        }

        return 0;
    }

    /**
     * 获取城防军的数量
     */
    private int getSoldierTeamCount(CrossChuHanCityData city) {
        return city.getExtraTeamNum() + city.getTeamNum();
    }

    // ============================== 移动 ==============================


    private boolean isCityInBattle(int cityId) {
        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
        if (city == null) {
            return false;
        }

        if (city.getBattlefield() == null) {
            return false;
        }

        return true;
    }

    /**
     * 强制进入城池
     */
    private void forceArriveNextCity(CrossChuHanTeam team, long arriveTime) {
        synchronized (lock) {
            CrossChuHanUserData userData = getUserData(team.getUserId());
            ArrayList<Integer> newRouteList = new ArrayList<>(team.getRouteList());
            int cityId = newRouteList.remove(0);
            CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);

            // 是否为自己主城
            boolean selfMainCity = false;
            ChuHanCityConfig cityConfig = config.getCityConfigMap().get(cityId);
            if (cityConfig.getType() == eChuHanCityType.Type1_MainCity.getValue() && cityConfig.getInitialCamp() == userData.getCampId()) {
                selfMainCity = true;
            }

            // 【进城池队列】
            if (!selfMainCity) { // 自己主城不需要进进攻防守
                CrossChuHanTeamArrive arrive = new CrossChuHanTeamArrive(team.getUserId(), team.getOccupation(), arriveTime);
                if (city.getCampId() == userData.getCampId()) {
                    // 进入防守队列
                    city.addDefTeamLast(arrive);
                } else {
                    // 进入进攻队列
                    city.addAtkTeamLast(arrive);
                }
            }

            // 【开战】
            boolean haveCreateBattlefield = false;
            if (city.getBattlefield() == null && city.getAtkTeamList().size() > 0 && city.getDefTeamList().isEmpty() && getSoldierTeamCount(city) == 0) {
                // 对方没人，直接拿下
                handleNoBattleOccupy(city, arriveTime);
                addNeedSyncCity(city.getCityId());
            } else if (city.getBattlefield() == null && city.getAtkTeamList().size() > 0) {
                // 开新战场
                createBattlefield(city);
                haveCreateBattlefield = true;
            } else if (city.getBattlefield() != null) {
                // 队伍加入战场
                teamToBattlefield(city, true);
            }

            boolean syncSelfToAll = !newRouteList.isEmpty();

            // 【修改队伍状态】
            team.setCityId(cityId);
            if (newRouteList.isEmpty() || city.getBattlefield() != null) {
                team.setBeginMoveTime(0);
                team.setRouteList(new ArrayList<>());
                team.setSmallMoveTime(0);
                team.setArriveTime(0);
                // 同步城池
                addNeedSyncCity(cityId);
            } else {
                moveByRoute(team, newRouteList, true);
            }

            // 日志
            CrossAutoLogMgr.add(new LogChuHanTeamMove(
                    config.getActivityInfo().getActivityId(),
                    team.getUserId(),
                    team.getOccupation(),
                    team.getCityId(),
                    team.getRouteList(),
                    groupId,
                    team.getArriveTime(),
                    getBuffAddition(userData.getCampId(), eChuHanBuffType.Type7_MoveSpeedUp).get(0)
            ));

            // 同步队伍
            if (haveCreateBattlefield) {
                syncCityTeamToSelf(city); // 城池开战，所有玩家改状态
            }
            if (syncSelfToAll) {
                addNeedSyncTeam(team); // 移动被打断，同步所有玩家
            }
            syncTeamToSelf(team.getUserId(), Collections.singletonList(team)); // 自己队伍要一直同步

            userData.setUpdateOption();
            crossChuHanData.setUpdateOption();
        }
    }

    /**
     * 计算下次到达时间
     */
    public long calcMoveTime(int campId, List<Integer> routeList) {
        // 最后一个城池，不是自己的，用慢速的时间，其余的用快速的
        long configTime = config.getMoveTimeFast();//改成秒了
        if (routeList.size() == 1) {
            Integer lastCityId = routeList.get(0);
            CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(lastCityId);
            if (city != null && city.getCampId() != campId) {
                configTime =  config.getMoveTime();
            }
        }

        // 改需求了,精度改成秒级
        Integer speedUp = getBuffAddition(campId, eChuHanBuffType.Type7_MoveSpeedUp).get(0);//减少千分比
        long resultTime = BigDecimal.valueOf(configTime).multiply(BigDecimal.valueOf(1000 - speedUp).divide(BigDecimal.valueOf(1000))).setScale(0, RoundingMode.DOWN).longValue();
        return resultTime * DateHelper.SECOND_MILLIONS;
    }

    /**
     * 出发
     * （不会同步队伍）
     */
    public void moveByRoute(CrossChuHanTeam team, List<Integer> routeList, boolean continueMove) {
        // 队伍从旧城池移除
        int cityId = team.getCityId();
        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
        CrossChuHanTeamArrive arrive = null;
        for (CrossChuHanTeamArrive tmp : new LinkedList<>(city.getDefTeamList())) {
            if (tmp.getUserId() == team.getUserId() && tmp.getTeamId() == team.getOccupation()) {
                arrive = tmp;
                break;
            }
        }
        if (arrive != null) {
            city.removeDefTeam(arrive);
        }

        // 出发
        if (!continueMove) {
            long now = System.currentTimeMillis();
            team.setBeginMoveTime(now);
            team.setRouteList(routeList);
            team.setSmallMoveTime(now);
            team.setArriveTime(now + calcMoveTime(getUserData(team.getUserId()).getCampId(), routeList));
        } else {
            team.setRouteList(routeList);
            team.setSmallMoveTime(team.getArriveTime());
            team.setArriveTime(team.getArriveTime() + calcMoveTime(getUserData(team.getUserId()).getCampId(), routeList));
        }
        getUserData(team.getUserId()).setUpdateOption();
        addTask(new CrossChuHanMoveTask(this, team.getArriveTime(), team.getUserId(), team.getOccupation()));
    }

    // ============================== 地图 ==============================

    private Map<Integer, CrossChuHanCityData> createCity() {

        Map<Integer, CrossChuHanCityData> cityMap = new ConcurrentHashMap<>();

        for (ChuHanCityConfig cityConfig : config.getCityConfigMap().values()) {
            ChuHanCityTypeConfig typeConfig = config.getCityTypeConfigMap().get(cityConfig.getType());

            CrossChuHanCityData city = new CrossChuHanCityData(getConfig().getActivityInfo().getActivityId(), groupId, cityConfig.getId());
            city.setCampId(cityConfig.getInitialCamp());
            city.setTeamNum(typeConfig.getTeamNum());
            city.setTeamSoldierNum(typeConfig.getSoldierNum());
            Long startTime = config.getStage1TimeList().get(0).get(0);
            city.setCollectionTime(startTime);
            city.setLastRecoveryExtraTeamTime(startTime);
            cityMap.put(cityConfig.getId(), city);
        }

        return cityMap;
    }

    private int getBirthCityId(CrossChuHanUserData userData) {
        if (userData == null) {
            return 0;
        }

        if (userData.getBirthCityId() != 0) {//改过出生地,判断有没有被占领或者战斗中
            CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(userData.getBirthCityId());
            if (city != null) {
                if (city.getCampId() == userData.getCampId()) {
                    if (!isCityInBattle(userData.getBirthCityId())) {
                        return userData.getBirthCityId();
                    }
                }
            }
        }

        int campId = userData.getCampId();
        return getCampBirthCity(campId);
    }

    private int getCampBirthCity(int campId) {
        for (ChuHanCityConfig cityConfig : config.getCityConfigMap().values()) {
            if (cityConfig.getType() == eChuHanCityType.Type1_MainCity.getValue() && cityConfig.getInitialCamp() == campId) {
                return cityConfig.getId();
            }
        }
        return 0;
    }

    /**
     * 前往城池
     */
    private int moveToCity(long userId, ChuHanProto.ChuHanMoveTeamReqMsg reqMsg) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_CHUHAN_NO_ABLE;
        }
        List<Integer> occupationList = reqMsg.getOccupationList();
        int toCityId = reqMsg.getToCityId();
        boolean isTargetCity = reqMsg.getIsTargetCity();

        // 时间判断
        eTongBingStage stage = this.config.getTongBingStage();
        if (stage != eTongBingStage.Stage1Time) {
            return GameErrorCode.E_CHUHAN_NO_OPERATE_TIME;
        }

        // 参数校验
        for (Integer occu : occupationList) {
            if (!userData.getTeamMap().containsKey(occu)) {
                return GameErrorCode.E_CHUHAN_TEAM_NO_EXIST;
            }
        }

        ChuHanCityConfig cityConfig = getConfig().getCityConfigMap().get(reqMsg.getToCityId());
        if (cityConfig == null) {
            return GameErrorCode.E_CHUHAN_CITY_NO_EXIST;
        }

        ChuHanCityConfig toCityConfig = getConfig().getCityConfigMap().get(toCityId);
        if (toCityConfig == null) {
            return GameErrorCode.E_CHUHAN_CITY_NO_EXIST;
        }

        if (getConfig().isPveCityByCityType(toCityConfig.getType())) {
            //PVE只能去自己阵营的PVE城池
            if (toCityConfig.getMapCamp() != userData.getCampId()) {
                return GameErrorCode.E_CHUHAN_PVE_CITY_CAMP_ERR;
            }

            if (!getConfig().isPveCityOpenTime(toCityConfig.getType())) {
                return GameErrorCode.E_CHUHAN_NO_OPERATE_TIME;
            }
        }

        synchronized (lock) {

            // 遍历所有队伍
            for (Integer occu : occupationList) {
                CrossChuHanTeam team = userData.getTeamMap().get(occu);

                // 跳过已经在这里的
                if (team.getCityId() == toCityId) {
                    continue;
                }
                // 前往城池
                int ret = moveToCity(team, toCityId);
                if (ret != 0) {
                    return ret;
                }
                if (isTargetCity) {
                    team.setTargetCityId(toCityId);
                }
            }
        }
        return 0;
    }

    /**
     * 前往城池
     */
    private int moveToCity(CrossChuHanTeam team, int toCityId) {
        ChuHanCityConfig toCityConfig = config.getCityConfigMap().get(toCityId);
        if (toCityConfig == null) {
            return GameErrorCode.E_CHUHAN_CITY_NO_EXIST;
        }
        // 时间判断
        eTongBingStage stage = this.config.getTongBingStage();
        if (stage != eTongBingStage.Stage1Time) {
            return GameErrorCode.E_CHUHAN_NO_OPERATE_TIME;
        }

        // 状态判断
        if (getTeamStatus(team) != eTongBingTeamStatus.Free) {
            return GameErrorCode.E_CHUHAN_TEAM_CHANGE_SOLDIER_NO_FREE;
        }

        // 判断是否达到兵力最低要求,不满足的跳过处理
        // 千分比最低兵力和固定最低兵力取最大值
        long minSoldierNum = BigDecimal.valueOf(team.getMaxSoldier())
                .multiply(BigDecimal.valueOf(config.getTeamNumLowerLimitParam()))
                .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_UP).longValue();
        if (team.getSoldierNum() < Math.max(minSoldierNum, config.getTeamNumLowerLimitFixedParam())) {
            return GameErrorCode.E_CHUHAN_TEAM_SOLDIER_MIN_LIMIT;
        }
        // 找路径
//        List<Integer> cityRouteList = getRouteList(team.getUserId(), team.getCityId(), toCityId);
        List<Integer> cityRouteList = getRouteListNew(team.getUserId(), team.getCityId(), toCityId);

        if (cityRouteList == null || cityRouteList.size() <= 1) {
            return GameErrorCode.E_CHUHAN_NO_ROUTE;
        }
        // 出发点不要
        cityRouteList.remove(0);

        // 移动
        moveByRoute(team, cityRouteList, false);
        addNeedSyncTeam(team);

        int campId = getUserData(team.getUserId()).getCampId();
        // 日志
        CrossAutoLogMgr.add(new LogChuHanTeamMove(
                config.getActivityInfo().getActivityId(),
                team.getUserId(),
                team.getOccupation(),
                team.getCityId(),
                team.getRouteList(),
                groupId,
                team.getArriveTime(),
                getBuffAddition(campId, eChuHanBuffType.Type7_MoveSpeedUp).get(0)
        ));
        return 0;
    }

    /**
     * 获取当前队伍到指定城池的路径
     */
    private List<Integer> getRouteList(long userId, int fromCityId, int toCityId) {
        ChuHanCityConfig toCityConfig = config.getCityConfigMap().get(toCityId);
        if (toCityConfig == null) {
            return null;
        }
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return null;
        }
        if (!checkCityCanSetTarget(userData.getCampId(), toCityId)) {
            return null;
        }
        ChuHanCityConfig curCityConfig = config.getCityConfigMap().get(fromCityId);
        // 能走的格子，未处于战斗状态
        Map<Integer, ChuHanMapNode> canMoveCityMap = new HashMap<>();
        // 能走的格子，处于战斗状态
        Map<Integer, ChuHanMapNode> battlefieldRouteMap = new HashMap<>();
        for (CrossChuHanCityData city : crossChuHanData.getCityMap().values()) {
            ChuHanMapNode node = config.getMapGraph().getNode(city.getCityId());
            if (node == null) {
                continue;
            }
            // 没有在战斗的己方城池
            if (city.getCampId() == userData.getCampId()) {
                ChuHanCityConfig safeCityConfig = config.getCityConfigMap().get(city.getCityId());
                if (city.getBattlefield() == null) {
                    canMoveCityMap.put(node.getCityId(), node);
                } else {
                    battlefieldRouteMap.put(node.getCityId(), node);
                }
            }
        }

        // 判断是否移除中心城
        if (System.currentTimeMillis() < config.getStage2BeginTime()) {
            for (ChuHanCityConfig cityConfig : config.getCityConfigMap().values()) {
                if (cityConfig.getType() == eChuHanCityType.Type6_CenterCity.getValue()) {
                    canMoveCityMap.remove(cityConfig.getId());
                    if (toCityId == cityConfig.getId()) {
                        return null;
                    }
                }
            }
        }

        // 起点强制加进去
        ChuHanMapNode startNode = getConfig().getMapGraph().getNode(curCityConfig.getId());
        canMoveCityMap.put(startNode.getCityId(), startNode);
        return ChuHanMapLogic.getRouteList(canMoveCityMap, battlefieldRouteMap, curCityConfig.getId(), toCityConfig.getId());
    }

    private List<Integer> getRouteListNew(long userId, int fromCityId, int toCityId) {
        ChuHanCityConfig toCityConfig = config.getCityConfigMap().get(toCityId);
        if (toCityConfig == null) {
            return null;
        }
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return null;
        }

        if (!checkCityCanSetTarget(userData.getCampId(), toCityId)) {
            return null;
        }

        List<ChuHanMapNode> paths = generalShortestPath(userData.getCampId(), getConfig().getMapGraph(), fromCityId, toCityId);
        List<Integer> routeList = new ArrayList<>();
        for (ChuHanMapNode node : paths) {
            routeList.add(node.getCityId());
        }
        return routeList;
    }

    private int speedUp(long userId, int occupation, int times) {
        // 参数校验
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            return GameErrorCode.E_CHUHAN_NO_ABLE;
        }
        CrossChuHanTeam team = userData.getTeamMap().get(occupation);
        if (team == null) {
            return GameErrorCode.E_CHUHAN_TEAM_NO_EXIST;
        }

        // 时间判断
        eTongBingStage stage = this.config.getTongBingStage();
        if (stage != eTongBingStage.Stage1Time) {
            return GameErrorCode.E_CHUHAN_NO_OPERATE_TIME;
        }

        // 取道具效果
        GoodsInfo goodsInfo = GoodsMgr.getGoodsInfo(config.getShenXingItem());
        if (goodsInfo == null) {
            return GameErrorCode.E_GOODS_NO_EXIST;
        }
        long reduceTime = goodsInfo.getParamList().get(0).longValue() * DateHelper.SECOND_MILLIONS;
        long totalReduceTime = times * reduceTime;
        long realReduceTime = 0L;
        long now = System.currentTimeMillis();

        synchronized (lock) {
            while (getTeamStatus(team) == eTongBingTeamStatus.Move) {
                if (realReduceTime + team.getArriveTime() - now < totalReduceTime) {
                    // 可以到达
                    realReduceTime = realReduceTime + team.getArriveTime() - now;
                    team.setArriveTime(now);
                    forceArriveNextCity(team, team.getArriveTime());
                } else {
                    // 无法到达
                    team.setArriveTime(team.getArriveTime() - (totalReduceTime - realReduceTime));
                    realReduceTime = totalReduceTime;
                    // 到达任务加回去
                    addTask(new CrossChuHanMoveTask(this, team.getArriveTime(), userId, occupation));
                    break;
                }
            }
        }

        // 真实消耗道具数量
        int realTimes = BigDecimal.valueOf(realReduceTime).divide(BigDecimal.valueOf(reduceTime), 0, BigDecimal.ROUND_UP).intValue();

        // 返回
        ChuHanProto.ChuHanMoveSpeedUpRespMsg.Builder resp = ChuHanProto.ChuHanMoveSpeedUpRespMsg.newBuilder();
        resp.setRet(0);
        resp.setTeam(parseChuHanTeamMsg(userData, team));

        ChuHanProto.CrossChuHanMoveSpeedUpRespMsg.Builder builder = ChuHanProto.CrossChuHanMoveSpeedUpRespMsg.newBuilder();
        builder.setResp(resp);
        builder.setReqTimes(times);
        builder.setRealTimes(realTimes);
        sendPacket(GameProtocol.S_CHUHAN_MOVE_SPEED_UP_FROM_CROSS, builder, userId);
        return 0;
    }

    private List<Integer> getMiddleCityId(int campId1, int campId2) {
        CrossChuHanData data = getCrossChuHanData();

        Set<Integer> cityList1 = new HashSet<>();
        Set<Integer> cityList2 = new HashSet<>();
        for (CrossChuHanCityData city : data.getCityMap().values()) {
            if (city.getCampId() == campId1) {
                cityList1.add(city.getCityId());
            } else if (city.getCampId() == campId2) {
                cityList2.add(city.getCityId());
            }
        }

        Set<Integer> middleCityId = new HashSet<>(ChuHanMapLogic.getMiddleCityId(config.getMapGraph(), cityList1, cityList2));

        return new ArrayList<>(middleCityId);
    }

    private int getCityTeam(long userId, ChuHanProto.ChuHanSyncCityTeamReqMsg reqMsg) {
        // 解析参数
        Map<Integer, Long> cityTimeMap = new ConcurrentHashMap<>();
        for (ChuHanProto.ChuHanSyncCityTeamMsg cityTeamMsg : reqMsg.getCityInfoList()) {
            cityTimeMap.put(cityTeamMsg.getCityId(), cityTeamMsg.getTime());
        }

        long now = System.currentTimeMillis();

        // 遍历全部玩家，把城池内移动中的队伍加进来
        Map<Integer, LinkedList<CrossChuHanShowTeam>> campTeamMap = new HashMap<>();
        for (CrossChuHanUserData userData : userDataMap.values()) {
            campTeamMap.putIfAbsent(userData.getCampId(), new LinkedList<>());

            for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                if (!cityTimeMap.containsKey(team.getCityId())) {
                    continue; // 跳过：不在该城市的队伍
                }

                if (getTeamStatus(team) == eTongBingTeamStatus.Move) {
                    campTeamMap.get(userData.getCampId()).add(new CrossChuHanShowTeam(userData, team));
                }
            }
        }

        // 排序
        CrossChuHanUserData myUserData = getUserData(userId);
        if (myUserData == null) {
            return 0;
        }
        int myCampId = myUserData.getCampId();
        String myUnionUid = myUserData.getUnionUid();
        for (LinkedList<CrossChuHanShowTeam> teamLinkedList : campTeamMap.values()) {
            teamLinkedList.sort(new Comparator<CrossChuHanShowTeam>() {
                @Override
                public int compare(CrossChuHanShowTeam o1, CrossChuHanShowTeam o2) {
                    CrossChuHanUserData userData1 = o1.getUserData();
                    CrossChuHanUserData userData2 = o2.getUserData();
                    // 【同阵营比较】
                    if (userData1.getCampId() == myCampId) {
                        // 自己的队伍优先
                        if (userData1.getUserId() == userId || userData2.getUserId() == userId) {
                            if (userData1.getUserId() != userData2.getUserId()) {
                                return userData1.getUserId() == userId ? 1 : -1;
                            }
                        }
                        // 同商会优先
                        boolean unionEq1 = Objects.equals(userData1.getUnionUid(), myUnionUid);
                        boolean unionEq2 = Objects.equals(userData2.getUnionUid(), myUnionUid);
                        if (unionEq1 || unionEq2) {
                            if (unionEq1 != unionEq2) {
                                return unionEq1 ? 1 : -1;
                            }
                        }
                    }
                    // 【通用比较】
                    // 战功优先
                    if (userData1.getZhanGong() != userData2.getZhanGong()) {
                        return userData1.getZhanGong() > userData2.getZhanGong() ? 1 : -1;
                    }
                    return 0;
                }
            }.reversed());
        }

        // 找需要发给客户端的
        int campTeamNum = config.getShowCampTopNum();
        List<CrossChuHanTeam> teamList = new ArrayList<>();
        for (LinkedList<CrossChuHanShowTeam> linkedList : campTeamMap.values()) {
            int num = 0;
            for (CrossChuHanShowTeam showTeam : linkedList) {
                if (num++ >= campTeamNum) {
                    break;
                }
                CrossChuHanTeam team = showTeam.getTeam();
                Long lReqTime = cityTimeMap.get(team.getCityId());
                if(null == lReqTime){
                    continue;
                }
                long updateMoveTime = team.getMoveUpdateTime();
                if (updateMoveTime - lReqTime > DateHelper.SECOND_MILLIONS * 60) {
                    teamList.add(team);
                }
            }
        }

        // 返回
        ChuHanProto.ChuHanSyncCityTeamRespMsg.Builder builder = ChuHanProto.ChuHanSyncCityTeamRespMsg.newBuilder();
        for (ChuHanProto.ChuHanSyncCityTeamMsg cityMsg : reqMsg.getCityInfoList()) {
            builder.addCityInfo(cityMsg.toBuilder().setTime(now));
        }
        for (CrossChuHanTeam team : teamList) {
            builder.addTeam(parseChuHanTeamMsg(getUserData(team.getUserId()), team));
        }
        sendPacket(Protocol.S_CHUHAN_SYNC_CITY_TEAM % 20000, builder, userId);
        return 0;
    }

    /**
     * 锁定商会
     */
    public void lockUnion(RankProto.CrossUnionRankListLockMsg lockMsg) {
        synchronized (lock) {
            if (getCrossChuHanData().isLockUnion()) {
                return;
            }
            List<CrossChuHanUnionJoin> rankList = new ArrayList<>();
            // 排序拿前几名 传过来的就是排序好的
            for (int i = 0; i < lockMsg.getRankList().size() && i < config.getUnionNum(); i++) {
                RankProto.CrossUnionRankLockTemp unionMsg = lockMsg.getRankList().get(i);
                // 【商会处理】
                CrossChuHanUnionJoin join = unionJoinMap.get(unionMsg.getRankTemp().getUnionUid());
                if (join == null) {
                    join = new CrossChuHanUnionJoin(config.getActivityInfo().getActivityId(), groupId, unionMsg.getRankTemp().getUnionUid());
                    join.setUnionBaseInfo(UnionBasePb.parseUnionBaseInfo(unionMsg.getRankTemp().getUnionBaseData()));
                    join.setMemberMap(new ConcurrentHashMap<>());
                    //已经拉过来是多少就是多少了，不用再去本服更新来更新去了，这个已经是历史最高赚钱了
                    join.setTotalAbility(Long.valueOf(unionMsg.getRankTemp().getValue()));
                    join.setInsertOption();
                    unionJoinMap.put(join.getUnionUid(), join);
                }
                join.setOldRank(i + 1);

                // 【成员处理】
                Map<Long, CrossChuHanUnionJoinMember> oldMap = join.getMemberMap();
                Map<Long, CrossChuHanUnionJoinMember> memberMap = new ConcurrentHashMap<>();
                for (RankProto.CrossUnionMemberTemp msg : unionMsg.getMemberList()) {
                    CrossChuHanUnionJoinMember member = oldMap.remove(msg.getMemberId());
                    if (member == null) {
                        member = new CrossChuHanUnionJoinMember(msg.getMemberId(), msg.getServerId());
                    }
                    member.setEarnSpeed(new BigInteger(msg.getEarnSpeed()).longValue());
                    member.setAbility(new BigInteger(msg.getMaxAbility()).longValue());
                    memberMap.put(member.getUserId(), member);
                    initUserData(member.getUserId(), join.getUnionUid());
                }
                join.setMemberMap(memberMap);

                rankList.add(join);
            }

            // 分阵营
            List<Integer> secondMatchRanges = config.secondMatchRanges();
            if (secondMatchRanges.isEmpty()) {
                logger.error("楚汉争霸 activity {} union lock error. 战力压缩没配置", config.getActivityInfo().getActivityId());
                return;
            }
            lockCamp(rankList, secondMatchRanges);
            // 改状态
            getCrossChuHanData().setLockUnion(true);
            logger.error("楚汉争霸 activity {} groupID {} lock union.", config.getActivityInfo().getActivityId(), groupId);

            // 移动玩家到起始点
            crossChuHanInitTeamPosTask();

            // 初始化榜单，让0值的也上榜，才能看到自己阵营都有谁
            initAllRankList();

            // 同步状态
            for (CrossChuHanUserData userData : userDataMap.values()) {
                syncBaseDataMsg(userData);
                // 跨服同步用户数据到游戏服
                syncUserDataToGameServer(userData);
            }

            sendGetUnionMemberMsg();
        }

        // 日志
        for (CrossChuHanUnionJoin join : unionJoinMap.values()) {
            CrossAutoLogMgr.add(new LogChuHanLockUnion(
                    config.getActivityInfo().getActivityId(),
                    join.getUnionUid(),
                    join.getCampId(),
                    join.getCompressedAbility().toString(),
                    join.getTotalAbility() + "",
                    groupId
            ));
        }
    }

    /**
     * 锁定阵营
     *
     * @param unionJoinList     参与活动的商会列表
     * @param secondMatchRanges 特殊分组参数
     *                          <p>
     *                          分配好第1名和第2名后，继续按照规则对分组内的兵力3.4名、5.6名、7.8名、9.10名、11.12名、13.14名商会进行分组，规则如下：
     *                          1	先对3.4名进行分配：第3名加入第2名的阵营，第4名加入第1名的阵营
     *                          <p>
     *                          2	再对5.6名进行分配：第5名加入当前总兵力较低的一方，第6名加入当前总兵力较高的一方
     *                          <p>
     *                          3	后续7.8名、9.10名、11.12名、13.14名的分配方式同上，前一名加入总兵力较低的一方，后一名加入总兵力较高的一方
     *                          <p>
     *                          4	最后，将15名~60名的商会按顺序填充至两方阵营，优先填充至总兵力较低的一方。直至一方阵营满30人后，剩余商会均填充至另一方阵营。
     */
    public void lockCamp(List<CrossChuHanUnionJoin> unionJoinList, List<Integer> secondMatchRanges) {
        synchronized (lock) {
            if (getCrossChuHanData().isLockUnion()) {
                return;
            }

            // 容错：先清空锁定数据
            for (CrossChuHanUserData userData : userDataMap.values()) {
                userData.setCampId(0);
            }
            for (CrossChuHanUnionJoin join : unionJoinMap.values()) {
                join.setCampId(0);
            }

            // 对所有商会进行战力压缩
            /**
             * 1	商会总兵力 = 商会所有成员的兵力相加
             *
             * 2	商会成员兵力计算方式
             * 	1	取玩家历史最高赚钱 / 5，得到5个队伍的平均赚钱
             *
             * 	2	根据5个队伍平均赚钱，配合战力转换公式，得到5个队伍能够提供的兵力
             *
             * 	3	最后再根据5个队伍的兵力 * 5，得到单名玩家能够提供的兵力
             */
            int campNum = config.getCampNum();
            List<CrossChuHanUnionJoin> afterSortUnions = new ArrayList<>();
            for (int i = 0; i < unionJoinList.size(); i++) {
                CrossChuHanUnionJoin union = unionJoinList.get(i);
                if (i < campNum) {
                    afterSortUnions.add(union);
                }
                BigDecimal totalAbility = BigDecimal.ZERO;
                for (CrossChuHanUnionJoinMember joinMember : union.getMemberMap().values()) {
                    long memberAbility = joinMember.getAbility();
                    if (memberAbility <= 0) {
                        continue;
                    }
                    long avgAbility = memberAbility / 5;
                    if (avgAbility <= 0) {
                        continue;
                    }
                    BigDecimal teamAbility = calcEquationTeamMaxSoldier(memberAbility / 5);
                    teamAbility = teamAbility.multiply(BigDecimal.valueOf(5));
                    totalAbility = totalAbility.add(teamAbility);
                }
                union.setCompressedAbility(totalAbility);
            }

            // 按压缩战力重新排序
            unionJoinList.sort((o1, o2) -> {
                if (o1.getCompressedAbility().compareTo(o2.getCompressedAbility()) == 0) { // 压缩战力一致，比较总赚钱
                    return Long.compare(o2.getTotalAbility(), o1.getTotalAbility());
                }
                return o2.getCompressedAbility().compareTo(o1.getCompressedAbility());
            });

            for (int i = 0; i < unionJoinList.size(); i++) {
                CrossChuHanUnionJoin unionJoin = unionJoinList.get(i);
                unionJoin.setCompressRank(i + 1);
                if (unionJoin.getOldRank() > campNum) {
                    afterSortUnions.add(unionJoin);
                }
            }

            Map<Integer, BigDecimal> campAbilityMap = new ConcurrentHashMap<>();
            Map<Integer, List<String>> campUnionMap = new ConcurrentHashMap<>();


            int campUnionNum = BigDecimal.valueOf(Math.min(config.getUnionNum(), unionJoinList.size())) // 配置数量和实际数量取最小
                    .divide(BigDecimal.valueOf(config.getCampNum()), 0, BigDecimal.ROUND_UP).intValue();
            List<Integer> campNumberList = new ArrayList<>();
            for (int campId = 1; campId <= campNum; campId++) {
                campNumberList.add(campId);
                campAbilityMap.put(campId, BigDecimal.ZERO);
            }
            Collections.shuffle(campNumberList);
            int campNumber1 = campNumberList.get(0);
            int campNumber2 = campNumberList.get(1);

            //1
            putJoinUnionIntoCamp(afterSortUnions, campAbilityMap, campUnionMap, 1, campNumber1);
            //2
            putJoinUnionIntoCamp(afterSortUnions, campAbilityMap, campUnionMap, 2, campNumber2);


            //第二次分配范围 3-xx
            int startRank = 3; //必须从第五名开始
            int endRank = secondMatchRanges.get(1);
            int secondMatchTimes = (endRank - startRank + 1) / campNum;
            for (int i = 0; i < secondMatchTimes; i++) {
                for (int j = 0; j < campNum; j++) {
                    int rank = startRank + i * campNum + j;
                    //目前只适合双阵营,再改多阵营要再改改
                    putJoinUnionIntoMinAbilityCamp(afterSortUnions, campAbilityMap, campUnionMap, rank);
                }
            }

            // 遍历剩余商会按加入最低的分配
            for (int i = endRank; i < unionJoinList.size(); i++) {
                int rank = i + 1;

                // 按 value 升序排序并提取 key 到 List
                List<Integer> campIdList = new ArrayList<>(campAbilityMap.keySet());

                campIdList.sort((o1, o2) -> {
                    //比较战力
                    BigDecimal camp1Ability = campAbilityMap.get(o1);
                    BigDecimal camp2Ability = campAbilityMap.get(o2);
                    int compare = camp1Ability.compareTo(camp2Ability);
                    if (compare == 0) {
                        //较商会数量 低的在前
                        int camp1UnionNum = campUnionMap.getOrDefault(o1, new ArrayList<>()).size();
                        int camp2UnionNum = campUnionMap.getOrDefault(o2, new ArrayList<>()).size();
                        compare = Integer.compare(camp1UnionNum, camp2UnionNum);
                        if (compare == 0) {
                            //一样的就随机一下
                            compare = randomHelper.next(2);
                        }
                    }
                    return compare;
                });

                for (Integer joinCampId : campIdList) {
                    // 跳过已经分配满的阵营
                    if (campUnionMap.get(joinCampId).size() >= campUnionNum) {
                        continue;
                    }
                    putJoinUnionIntoCamp(afterSortUnions, campAbilityMap, campUnionMap, rank, joinCampId);
                    break;
                }
            }
            // 阵营数据生成
            initCampUserData();

            refreshOrderCity();
        }
    }

    /**
     * 把商会放入指定阵营
     *
     * @param unionJoinList
     * @param campAbilityMap
     * @param campUnionMap
     * @param rank
     * @param campId
     */
    private void putJoinUnionIntoCamp(List<CrossChuHanUnionJoin> unionJoinList, Map<Integer, BigDecimal> campAbilityMap, Map<Integer, List<String>> campUnionMap, int rank, int campId) {
        if (campId == 0) {
            return;
        }
        if (rank < 1 || rank > unionJoinList.size()) {
            return;
        }
        CrossChuHanUnionJoin unionJoin = unionJoinList.get(rank - 1);

        unionJoinCampMap(campAbilityMap, campUnionMap, campId, unionJoin);
    }

    private void unionJoinCampMap(Map<Integer, BigDecimal> campAbilityMap, Map<Integer, List<String>> campUnionMap, int campId, CrossChuHanUnionJoin unionJoin) {
        BigDecimal campAbility = campAbilityMap.get(campId);
        if (campAbility == null) {
            campAbility = BigDecimal.ZERO;
            campAbilityMap.put(campId, campAbility);
        }
        campAbility = campAbility.add(unionJoin.getCompressedAbility());
        campAbilityMap.put(campId, campAbility);

        List<String> unionIds = campUnionMap.get(campId);
        if (unionIds == null) {
            unionIds = new ArrayList<>();
            campUnionMap.put(campId, unionIds);
        }
        unionIds.add(unionJoin.getUnionUid());

        setUnionJoinCamp(unionJoin, campId);
    }

    private void setUnionJoinCamp(CrossChuHanUnionJoin unionJoin, int campId) {
        if (unionJoin == null) {
            return;
        }

        unionJoin.setCampId(campId);
        for (CrossChuHanUnionJoinMember member : unionJoin.getMemberMap().values()) {
            CrossChuHanUserData userData = getUserData(member.getUserId());
            if (userData != null) {
                userData.setCampId(campId);
            }
        }
    }

    /**
     * 阵营数量一样时把商会放入最低战力阵营,不一样放入最少的
     *
     * @param unionJoinList
     * @param campAbilityMap
     * @param campUnionMap
     * @param rank
     * @return 返回加入的阵营
     */
    private int putJoinUnionIntoMinAbilityCamp(List<CrossChuHanUnionJoin> unionJoinList, Map<Integer, BigDecimal> campAbilityMap, Map<Integer, List<String>> campUnionMap, int rank) {
        if (rank < 1 || rank > unionJoinList.size()) {
            return 0;
        }
        CrossChuHanUnionJoin unionJoin = unionJoinList.get(rank - 1);

        List<Integer> campIdList = new ArrayList<>(campAbilityMap.keySet());

        campIdList.sort((o1, o2) -> {
            //先比较商会数量 低的在前
            int camp1UnionNum = campUnionMap.getOrDefault(o1, new ArrayList<>()).size();
            int camp2UnionNum = campUnionMap.getOrDefault(o2, new ArrayList<>()).size();
            int compare = Integer.compare(camp1UnionNum, camp2UnionNum);
            if (compare == 0) {
                //商会数量一样的比较战力
                BigDecimal camp1Ability = campAbilityMap.get(o1);
                BigDecimal camp2Ability = campAbilityMap.get(o2);
                compare = camp1Ability.compareTo(camp2Ability);
                if (compare == 0) {
                    //战力一样的就随机一下
                    compare = randomHelper.next(2);
                }
            }
            return compare;
        });

        Integer joinCampId = campIdList.get(0);
        unionJoinCampMap(campAbilityMap, campUnionMap, joinCampId, unionJoin);
        return joinCampId;
    }

    private int getMinCampId(Map<Integer, BigDecimal> campAbilityMap) {
        if (campAbilityMap.isEmpty()) {
            return 1;
        }
        int minCampId = 0;
        BigDecimal minCampAbility = BigDecimal.ZERO;
        for (Map.Entry<Integer, BigDecimal> entry : campAbilityMap.entrySet()) {
            int campId = entry.getKey();
            BigDecimal campAbility = entry.getValue();
            if (minCampId == 0) {
                minCampId = campId;
                minCampAbility = campAbility;
            } else {
                if (campAbility.compareTo(minCampAbility) < 0) {
                    minCampId = campId;
                    minCampAbility = campAbility;
                } else if (campAbility.compareTo(minCampAbility) == 0) {//能力一样就随机一下
                    if (randomHelper.next(2) == 0) {
                        minCampId = campId;
                        minCampAbility = campAbility;
                    }
                }
            }
        }
        return minCampId;
    }

    /**
     * 是否有资格
     */
    private boolean isAble(long userId) {
        return userDataMap.get(userId) != null;
    }

    // ============================== 红点 ==============================

    /**
     * 添加红点
     */
    private void addRedDot(CrossChuHanUserData userData, eTongBingRedDotType type) {
        if (userData == null) {
            return;
        }
        if (userData.getRedDotMap().containsKey(type.getValue())) {
            return;
        }
        userData.getRedDotMap().put(type.getValue(), System.currentTimeMillis());
        userData.setUpdateOption();

        ChuHanProto.ChuHanSyncRedDotRespMsg.Builder builder = ChuHanProto.ChuHanSyncRedDotRespMsg.newBuilder();
        builder.addType(type.getValue());
        sendPacket(Protocol.S_CHUHAN_SYNC_RED_DOT % 20000, builder, userData.getUserId());
    }

    // ============================== buff ==============================

    /**
     * 获取buff效果
     *
     * @return 看配置下标是哪个就拿哪个
     */
    private List<Integer> getBuffAddition(long userId, eChuHanBuffType buffType) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            List<Integer> resultList = new ArrayList<>();
            resultList.add(0);
            resultList.add(0);
            return resultList;
        }
        return getBuffAddition(userData.getCampId(), buffType);
    }


    /**
     * 获取buff效果
     *
     * @return 看配置下标是哪个就拿哪个
     */
    private List<Integer> getBuffAddition(int campId, eChuHanBuffType buffType) {
        // 目前最多一个buff有2个效果
        List<Integer> resultList = new ArrayList<>();
        resultList.add(0);
        resultList.add(0);

        // 判断阵营
        if (campId == 0) {
            return resultList;
        }

        CrossChuHanCampData campData = campDataMap.get(campId);
        if (campData == null) {
            return resultList;
        }

        for (CrossChuHanCampBuff campBuff : campData.getBuffMap().values()) {
            ChuHanBuffTypeConfig buffTypeConfig = getConfig().getBuffTypeConfigMap().get(campBuff.getBuffId());
            if (buffTypeConfig == null) {
                continue;
            }
            if (buffTypeConfig.getType() != buffType.getValue()) {
                continue;
            }

            int buffLevel = campBuff.getLevel();
            if (buffTypeConfig.getType() != eChuHanBuffType.Type11_BuffLevelAdd.getValue() && getConfig().getType11BuffEffectIds().contains(buffTypeConfig.getId())) {
                buffLevel += getBuffAddition(campId, eChuHanBuffType.Type11_BuffLevelAdd).get(0);
            }

            List<Integer> buffValues = buffTypeConfig.getBuffParamsByLevel(buffLevel);
            for (int i = 0; i < buffValues.size() && i < resultList.size(); i++) {
                resultList.set(i, resultList.get(i) + buffValues.get(i));
            }
        }
        return resultList;
    }

    /**
     * 获得生效的BUFFid 和计算的等级
     * @param campId
     * @return
     */
    public List<List<Integer>> getEffectCampBuffList(int campId) {
        // 目前最多一个buff有2个效果
        List<List<Integer>> resultList = new ArrayList<>();

        // 判断阵营
        if (campId == 0) {
            return resultList;
        }

        CrossChuHanCampData campData = campDataMap.get(campId);
        if (campData == null) {
            return resultList;
        }


        for (CrossChuHanCampBuff campBuff : campData.getBuffMap().values()) {
            ChuHanBuffTypeConfig buffTypeConfig = getConfig().getBuffTypeConfigMap().get(campBuff.getBuffId());
            if (buffTypeConfig == null) {
                continue;
            }

            int buffLevel = campBuff.getLevel();
            if (buffTypeConfig.getType() != eChuHanBuffType.Type11_BuffLevelAdd.getValue() && getConfig().getType11BuffEffectIds().contains(buffTypeConfig.getId())) {
                buffLevel += getBuffAddition(campId, eChuHanBuffType.Type11_BuffLevelAdd).get(0);
            }

            List<Integer> buffInfos = new ArrayList<>();
            buffInfos.add(campBuff.getBuffId());
            buffInfos.add(buffLevel);

            resultList.add(buffInfos);
        }
        return resultList;
    }

    /**
     * 根据城池占领情况刷新阵营BUFF
     *
     * @param campId
     * @return
     */
    private void refreshCampBuff(int campId) {
        CrossChuHanCampData campData = campDataMap.get(campId);
        if (campData == null) {
            logger.error("refreshCampBuff campId:{} not exist", campId);
            return;
        }
        Map<Integer, CrossChuHanCampBuff> buffMap = new ConcurrentHashMap<>(); //同个BUFFid 会叠加，每叠加一次升一级
        for (CrossChuHanCityData city : crossChuHanData.getCityMap().values()) {
            if (city.getCampId() != campId) {
                continue;
            }

            ChuHanCityConfig cityConfig = getConfig().getCityConfigMap().get(city.getCityId());
            if (cityConfig != null) {
                mergeCampBuff(cityConfig.getBuffId(), buffMap, campId);
            }

            ChuHanCityTypeConfig cityTypeConfig = getConfig().getCityTypeConfigMap().get(cityConfig.getType());
            if (cityTypeConfig != null) {
                mergeCampBuff(cityTypeConfig.getBuffEffect(), buffMap, campId);
            }
        }

        //还有PVE获得的BUFF

        for (Integer buffId : campData.getPveBuffMap().values()) {
            mergeCampBuff(buffId, buffMap, campId);
        }

        campData.setBuffMap(buffMap);
    }

    private void mergeCampBuff(int cityTypeConfig, Map<Integer, CrossChuHanCampBuff> buffMap, int campId) {
        ChuHanBuffTypeConfig buffConfig = getConfig().getBuffTypeConfigMap().get(cityTypeConfig);
        if (buffConfig != null) {
            CrossChuHanCampBuff campBuff = buffMap.get(buffConfig.getId());
            if (campBuff == null) {
                campBuff = new CrossChuHanCampBuff(campId, buffConfig.getId());
                buffMap.put(buffConfig.getId(), campBuff);
            } else {
                campBuff.setLevel(campBuff.getLevel() + 1);
            }
        }
    }

    private void beforeEndBuff(int buffId, int campId, long dealTime) {
        ChuHanBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buffId);
        if (buffTypeConfig == null) {
            return;
        }
        if (buffTypeConfig.getType() == eChuHanBuffType.Type4_Hospital.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksHospitalRecovery(userData.getUserId());
                }
            }
        } else if (buffTypeConfig.getType() == eChuHanBuffType.Type5_CityFoodProduce.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateCityFoodProdProduction(campId, dealTime);
                }
            }
        }
    }

    private void afterEndBuff(int buffId, int campId) {
        ChuHanBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buffId);
        if (buffTypeConfig == null) {
            return;
        }
        if (buffTypeConfig.getType() == eChuHanBuffType.Type1_TeamSoldierMaxNum.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    refreshTeam(userData);
                }
            }
        } else if (buffTypeConfig.getType() == eChuHanBuffType.Type3_CityTeamMaxNum.getValue()) {
            synchronized (lock) {
                int addTeamNum = getBuffAddition(campId, eChuHanBuffType.Type3_CityTeamMaxNum).get(0);
                for (CrossChuHanCityData city : getCrossChuHanData().getCityMap().values()) {
                    if (city.getCampId() == campId) {
                        int num = Math.min(city.getTeamNum(), addTeamNum);
                        city.setTeamNum(city.getTeamNum() - num);
                    }
                }
            }
        } else if (buffTypeConfig.getType() == eChuHanBuffType.Type4_Hospital.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksHospitalWoundedMaxNum(userData.getUserId());
                    recalculateBarracksHospitalWoundedCount(userData.getUserId());
                }
            }
        } else if (buffTypeConfig.getType() == eChuHanBuffType.Type6_ReadySoldierMaxNum.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksReadyMaxNum(userData.getUserId());
                }
            }
        }
    }

    private void beforeBeginBuff(int buffId, int campId, long dealTime) {
        ChuHanBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buffId);
        if (buffTypeConfig == null) {
            return;
        }
        if (buffTypeConfig.getType() == eChuHanBuffType.Type4_Hospital.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksHospitalRecovery(userData.getUserId());
                }
            }
        } else if (buffTypeConfig.getType() == eChuHanBuffType.Type5_CityFoodProduce.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateCityFoodProdProduction(campId, dealTime);
                }
            }
        }
    }

    private void afterBeginBuff(int buffId, int campId) {
        ChuHanBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buffId);
        if (buffTypeConfig == null) {
            return;
        }
        if (buffTypeConfig.getType() == eChuHanBuffType.Type1_TeamSoldierMaxNum.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    refreshTeam(userData);
                }
            }
        }else if (buffTypeConfig.getType() == eChuHanBuffType.Type3_CityTeamMaxNum.getValue()) {
            synchronized (lock) {
                for (CrossChuHanCityData city : getCrossChuHanData().getCityMap().values()) {
                    if (city.getCampId() == campId) {
                        city.setTeamNum(city.getTeamNum() + getBuffAddition(campId, eChuHanBuffType.Type3_CityTeamMaxNum).get(0));
                        if (city.getBattlefield() != null) {
                            teamToBattlefield(city, true);
                        }
                    }
                }
            }
        } else if (buffTypeConfig.getType() == eChuHanBuffType.Type4_Hospital.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksHospitalWoundedMaxNum(userData.getUserId());
                    recalculateBarracksHospitalWoundedCount(userData.getUserId());
                }
            }
        } else if (buffTypeConfig.getType() == eChuHanBuffType.Type6_ReadySoldierMaxNum.getValue()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksReadyMaxNum(userData.getUserId());
                }
            }
        } else if (buffTypeConfig.getType() == eChuHanBuffType.Type11_BuffLevelAdd.getValue()) {
            //获得后其他的都加一级，都跑一遍BUFF更新
            CrossChuHanCampData campData = campDataMap.get(campId);
            if (campData != null) {
                for (CrossChuHanCampBuff campBuff : campData.getBuffMap().values()) {
                    ChuHanBuffTypeConfig checkBuffConfig = getConfig().getBuffTypeConfigMap().get(campBuff.getBuffId());
                    if (checkBuffConfig == null) {
                        continue;
                    }
                    if (checkBuffConfig.getType() == eChuHanBuffType.Type11_BuffLevelAdd.getValue()) {
                        continue;
                    }
                    afterBeginBuff(checkBuffConfig.getId(), campId);
                }
            }
        }
    }

    private long getSkillSoldierAddition(long userId, int occupation) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return 0;
        }
        List<Long> list = userData.getSoldierAddition();
        long add = 0;
        if (list.size() >= occupation) {
            add = list.get(occupation - 1);
        } else {

        }
        if (list.size() > 5) {
            add += list.get(5);
        }
        return add;
    }

    // ============================== 队伍 ==============================

    /**
     * 更新门客数据（没资格也可以更新，不然游戏第一次进来没有值）
     *
     * @param first               首次同步不用设置CD，队伍士兵补满
     * @param isRefreshMaxSoldier 是否只刷新兵力上限
     */
    private int updatePatronsAbility(CrossChuHanUserData userData, boolean first, Map<Integer, CrossChuHanPatrons> patronsMap, List<Long> addition, boolean isRefreshMaxSoldier) {
        synchronized (lock) {

            if (!isRefreshMaxSoldier) {
                if (!first && System.currentTimeMillis() - userData.getLastSyncPatronsTime() < config.getSyncPatronsCd()) {
                    return GameErrorCode.E_CHUHAN_REQ_FREQUENTLY;
                }
                for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                    if (getTeamStatus(team) != eTongBingTeamStatus.Free) {
                        return GameErrorCode.E_CHUHAN_TEAM_NO_FREE;
                    }
                }
            }

            userData.setSoldierAddition(addition);
            userData.setPatronsMap(patronsMap);

            refreshTeam(userData);

            // 只刷新兵力上限不返回兵营
            if (!isRefreshMaxSoldier) {
                // 士兵回兵营
                Map<Integer, CrossChuHanTeam> teamMap = userData.getTeamMap();
                CrossChuHanBarracks barracks = userData.getBarracks();
                for (CrossChuHanTeam team : teamMap.values()) {
                    long soldierNum = team.getSoldierNum();
                    team.setSoldierNum(0);
                    barracks.setReadyNum(barracks.getReadyNum() + soldierNum);
                }

                if (first) {
                    // 【首次同步】
                    // 兵补充满
                    for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                        team.setSoldierNum(team.getMaxSoldier());
                    }
                } else {
                    // 【非首次同步】
                    userData.setLastSyncPatronsTime(System.currentTimeMillis());
                }
            }
        }

        userData.setUpdateOption();

        syncBaseDataMsg(userData);

        if (!isRefreshMaxSoldier) {//首次或者手动同步战力加日志
            List<JSONObject> teamSoldierNumList = new ArrayList<>();
            for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                JSONObject teamInfo = new JSONObject();
                teamInfo.put("id", team.getOccupation());
                teamInfo.put("num", team.getMaxSoldier());
                teamSoldierNumList.add(teamInfo);
            }
            CrossAutoLogMgr.add(new LogChuHanUpdateAbility(
                    userData.getActivityId(),
                    userData.getUserId(),
                    userData.getGroupId(),
                    userData.getCampId(),
                    userData.getUnionUid(),
                    teamSoldierNumList
            ));

        }
        return 0;
    }

    /**
     * 刷新队伍主将、兵力
     */
    private void refreshTeam(CrossChuHanUserData userData) {
        Map<Integer, List<CrossChuHanPatrons>> listMap = new ConcurrentHashMap<>();
        for (CrossChuHanPatrons patrons : userData.getPatronsMap().values()) {
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patrons.getId());
            if (patronsInfo != null) {
                listMap.putIfAbsent(patronsInfo.getOccupation(), new ArrayList<>());
                listMap.get(patronsInfo.getOccupation()).add(patrons);
            }
        }

        for (CrossChuHanTeam team : userData.getTeamMap().values()) {
            List<CrossChuHanPatrons> list = listMap.getOrDefault(team.getOccupation(), new ArrayList<>());
            List<Integer> rankList = list.stream().sorted(Comparator.comparing(CrossChuHanPatrons::getAbility).reversed()).map(CrossChuHanPatrons::getId).collect(Collectors.toList());

            if (rankList.isEmpty()) {
                continue;
            }
            // 重新分配主将副将
            team.setPatronsList(rankList);

            // 重新计算上限
            team.setMaxSoldier(getTeamMaxSoldier(userData, team));
        }

        // 这个变了之后，兵营也会跟这边
        recalculateBarracksReadyMaxNum(userData.getUserId());

        userData.setUpdateOption();

        // 同步队伍数和兵营数据
        syncTeamToSelf(userData.getUserId(), new ArrayList<>(userData.getTeamMap().values()));
        syncBarracksMsg(userData.getUserId());
    }

    private long calcUserTeamTotalAbility(CrossChuHanUserData userData, CrossChuHanTeam team) {
        // 拿门客赚钱
        long totalAbility = 0L;
        List<Integer> patronsList = team.getPatronsList();
        // 只取前6个统计总赚钱（主将+副将数量=6）
        int count = Math.min(patronsList.size(), config.getTeamMainPatronsNum() + config.getTeamAuxiliaryPatronsNum());
        for (int i = 0; i < count; i++) {
            Integer patronsId = team.getPatronsList().get(i);
            CrossChuHanPatrons patrons = userData.getPatronsMap().get(patronsId);
            if (patrons != null) {
                totalAbility += patrons.getAbility();
            }
        }
        if (totalAbility <= 0) {
            return 0;
        }
        return totalAbility;
    }

    /**
     * 用队伍赚钱根据公司得到带兵上限
     *
     * @param teamAbility
     * @return
     */
    private BigDecimal calcEquationTeamMaxSoldier(long teamAbility) {
        // 【计算】
        BigDecimal paramAbility = BigDecimal.valueOf(teamAbility);
        BigDecimal param1 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(0));
        BigDecimal param2 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(1));
        BigDecimal param3 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(2));
        BigDecimal param4 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(3));
        BigDecimal param5 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(4));

        BigDecimal param6 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(5));
        BigDecimal param7 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(6));
        BigDecimal param8 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(7));
        BigDecimal param9 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(8));
        BigDecimal param10 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(9));
        BigDecimal param11 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(10));
        BigDecimal param12 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(11));

        // log x param2
        double logValue = Math.log(paramAbility.divide(param1, 8, BigDecimal.ROUND_DOWN).doubleValue());
        BigDecimal logPart = BigDecimal.valueOf(logValue).multiply(param2.divide(param3, 8, BigDecimal.ROUND_DOWN)).setScale(8, BigDecimal.ROUND_DOWN);

        // 左边一大串
        //取max
        BigDecimal leftPart = BigDecimal.valueOf(Math.max(logPart.doubleValue(), param4.doubleValue()));

        // 取完max乘 参数5/参数6
        leftPart = leftPart.multiply(param5.divide(param6, 8, BigDecimal.ROUND_DOWN)).setScale(8, BigDecimal.ROUND_DOWN);

        BigDecimal rightPart = BigDecimal.valueOf(Math.pow(paramAbility.doubleValue(), param7.divide(param8, 8, BigDecimal.ROUND_DOWN).doubleValue()));
        rightPart = rightPart.multiply(param9.divide(param10, 8, BigDecimal.ROUND_DOWN)).setScale(8, BigDecimal.ROUND_DOWN);
        //取max
        rightPart = BigDecimal.valueOf(Math.max(rightPart.doubleValue(), param4.doubleValue()));
        rightPart = rightPart.multiply(param11.divide(param12, 8, BigDecimal.ROUND_DOWN)).setScale(8, BigDecimal.ROUND_DOWN);

        leftPart = leftPart.add(rightPart);

        return leftPart;
    }

    private long getTeamMaxSoldier(CrossChuHanUserData userData, CrossChuHanTeam team) {
        // 拿门客赚钱
        long teamAbility = calcUserTeamTotalAbility(userData, team);
        BigDecimal leftPart = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(3));
        try {
            leftPart = calcEquationTeamMaxSoldier(teamAbility);
        }catch (Exception e) {
            logger.error("getTeamMaxSoldier error, teamAbility:{}, userId:{}, occupation:{} err:{}", teamAbility, team.getUserId(), team.getOccupation(), e);
        }

        // 拿buff加成
        int buffAddition = getBuffAddition(userData.getUserId(), eChuHanBuffType.Type1_TeamSoldierMaxNum).get(0);
        long skillSoldierAddition = getSkillSoldierAddition(userData.getUserId(), team.getOccupation());

        // 加buff加成
        leftPart = leftPart
                .add(BigDecimal.valueOf(skillSoldierAddition))
                .add(BigDecimal.valueOf(buffAddition));

        // 返回
        return leftPart.setScale(0, BigDecimal.ROUND_DOWN).longValue();
    }

    private CrossChuHanTeam getTeamByArrive(CrossChuHanTeamArrive arrive) {
        return getUserData(arrive.getUserId()).getTeamMap().get(arrive.getTeamId());
    }

    private eTongBingTeamStatus getTeamStatus(CrossChuHanTeam team) {
        if (team.getRouteList().size() > 0) {
            return eTongBingTeamStatus.Move;
        }
        if (team.getCityId() == 0) { // 未分配阵营时为0
            return eTongBingTeamStatus.Free;
        }

        // 在主城时，都为空闲
        int campId = getUserData(team.getUserId()).getCampId();
        ChuHanCityConfig cityConfig = config.getCityConfigMap().get(team.getCityId());
        if (cityConfig.getType() == eChuHanCityType.Type1_MainCity.getValue() && cityConfig.getInitialCamp() == campId) {
            return eTongBingTeamStatus.Free;
        }

        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(team.getCityId());
        if (city.getBattlefield() != null) {
            return eTongBingTeamStatus.Battle;
        }
        return eTongBingTeamStatus.Free;
    }

    private List<CrossChuHanTeam> getTopTeam() {
        List<CrossChuHanTeam> result = new ArrayList<>();

        int campTopNum = config.getLightRankNum();

        for (CrossChuHanRankListModel rankListModel : campUserRankMap.values()) {
            int num = 0;
            for (CrossChuHanRank rank : rankListModel.getRankList()) {
                if (++num > campTopNum) {
                    break;
                }
                CrossChuHanUserData userData = getUserData(Long.valueOf(rank.getId()));
                for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                    if (getTeamStatus(team) == eTongBingTeamStatus.Move) {
                        result.add(team);
                    }
                }
            }
        }

        return result;
    }

    private int needHelp(long userId, ChuHanProto.ChuHanNeedHelpReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();

        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
        if (city == null) {
            return GameErrorCode.E_CHUHAN_CITY_NO_EXIST;
        }

        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            return GameErrorCode.E_CHUHAN_NO_ABLE;
        }

        synchronized (lock) {

            long now = System.currentTimeMillis();

            // 冷却
            if (now - userData.getNeedHelpTime() < config.getNeedHelpCd()) {
                return GameErrorCode.E_CHUHAN_NEED_HELP_ON_CD;
            }

            // 是否战斗中
            CrossChuHanBattlefield battlefield = city.getBattlefield();
            if (battlefield == null) {
                return GameErrorCode.E_CHUHAN_NO_BATTLE;
            }

            // 看看有没有自己的阵营在里面
            int myCampId = userData.getCampId();
            boolean hasMyCamp = false;
            if (city.getCampId() == myCampId) {
                hasMyCamp = true;
            }
            if (!hasMyCamp) {
                for (CrossChuHanTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
                    if (getUserData(arrive.getUserId()).getCampId() == myCampId) {
                        hasMyCamp = true;
                        break;
                    }
                }
            }
            if (!hasMyCamp) {
                for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                    for (CrossChuHanBattlefieldTeam tempTeam : new LinkedList<>(part.getAtkList())) {
                        if (myCampId == getUserData(tempTeam.getTeam().getUserId()).getCampId()) {
                            hasMyCamp = true;
                            break;
                        }
                    }
                }
            }
            if (!hasMyCamp) {
                return GameErrorCode.E_CHUHAN_NO_MY_CAMP_TEAM;
            }

            // 求援
            battlefield.setHelpNum(battlefield.getHelpNum() + 1);
            city.setUpdateOption();

            // 设置CD
            userData.setNeedHelpTime(now);

            // 同步城池
            addNeedSyncCity(cityId);
        }

        // 同步求援消息
        addSystemChat(eMessageType.ChuHanCampHelpChat, userId, userData.getCampId(),
                (userData.getCampId() != city.getCampId() ? 1 : 2) + GameConfig.TALK_SEPARATOR + String.valueOf(city.getCityId()),
                System.currentTimeMillis());

        return 0;
    }

    // ============================== CMD ==============================

    public void loginByCmd(long userId, ChuHanProto.CrossChuHanLoginReqMsg reqMsg) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            ChuHanProto.ChuHanSyncBaseDataRespMsg.Builder builder = ChuHanProto.ChuHanSyncBaseDataRespMsg.newBuilder();
            builder.setJoinStatus(3); // 3商会未入选
            builder.setSyncPatronsTime(0);
            builder.setGuide(0);
            builder.setSyncNeedHelpTime(0);
            sendPacket(Protocol.S_CHUHAN_SYNC_BASE_DATA % 20000, builder, userId);
            return;
        }
        // 第一次帮他同步
        if (userData.getPatronsMap().isEmpty()) {
            updatePatronsAbility(userData, true, CrossChuHanPb.parseCrossChuHanPatrons(reqMsg.getPatronsListList()), reqMsg.getSoldierAdditionList(), false);
        }
        // 同步数据
        syncBaseDataMsg(userData);
        // 跨服同步用户数据到游戏服
        syncUserDataToGameServer(userData);
        //任命了别人别人可能没在线，登录的时候再同步一次
        syncUserPosition(userId);
        // 同步战功成就
        notifyConditionChange(userId, eGamePlayerEventType.ChuHanZhanGong, userData.getZhanGong());
        notifyConditionChange(userId, eGamePlayerEventType.ChuHanUnionZhanGong, getUnionZhanGong(userData.getUnionUid()));
        // 同步全部红点
        if (config.inEnterActivityTime()) {
            ChuHanProto.ChuHanSyncRedDotRespMsg.Builder builder = ChuHanProto.ChuHanSyncRedDotRespMsg.newBuilder();
            builder.addAllType(userData.getRedDotMap().keySet());
            sendPacket(Protocol.S_CHUHAN_SYNC_RED_DOT % 20000, builder, userData.getUserId());
        }
        // 同步高亮玩家
        syncLight();
        // 同步阵营指挥官
        syncCommanderInfo(userId, userData.getCampId());

        //同步每日军令任务
        int day = 0;
        if (ActivityMgr.activityInTime(getConfig().getActivityInfo())) {
            day = ActivityHelper.getWhichDayAfterBeginTime(getConfig().getActivityInfo());
        }else {
            day = ActivityHelper.getWhichDayAfterBeginTime(getConfig().getActivityInfo(), getConfig().getActivityInfo().getEndTime());
        }

        syncOrderCityTask(day, userData);

    }

    public void syncOrderCityTaskToAll() {
        if (isNeedSyncOrderCityTask.compareAndSet(true, false)) {
            int day = ActivityHelper.getWhichDayAfterBeginTime(getConfig().getActivityInfo());
            for (CrossChuHanUserData userData : userDataMap.values()) {
                syncOrderCityTask(day, userData);
            }
        }
    }

    /**
     * 同步每日军令任务
     */
    private void syncOrderCityTask(int day, CrossChuHanUserData userData) {
        if (userData == null) {
            return;
        }

        ChuHanProto.ChuHanOrderCityTaskSyncMsg.Builder syncMsg = ChuHanProto.ChuHanOrderCityTaskSyncMsg.newBuilder();
        CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
        if (campData == null) {
            return;
        }

        CrossChuHanOrderCity orderCity = crossChuHanData.getOrderCityMap().get(day);
        if (orderCity == null) {
            return;
        }

        for (Map.Entry<Integer, List<Integer>> entry : orderCity.getOrderCityMap().entrySet()) {
            int orderType = entry.getKey();
            ChuHanProto.ChuHanOrderCityTaskTemp.Builder taskBuilder = ChuHanProto.ChuHanOrderCityTaskTemp.newBuilder();
            taskBuilder.setOrderCityType(orderType);
            List<Integer> cityIdList = entry.getValue();
            for (Integer cityId : cityIdList) {
                ChuHanProto.ChuHanOrderCityTemp.Builder cityBuilder = ChuHanProto.ChuHanOrderCityTemp.newBuilder();
                cityBuilder.setCityId(cityId);
                cityBuilder.setIsOccupy(campData.isOccupyByDay(day, cityId));
                taskBuilder.addCityInfos(cityBuilder);
            }
            syncMsg.addTaskInfos(taskBuilder);
        }

        sendPacket(GameProtocol.S_CHUHAN_SYNC_ORDER_CITY_TASK_FROM_CROSS, syncMsg, userData.getUserId());

    }

    private void syncUserPosition(long userId) {
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        if (userBaseInfo != null) {
            CrossChuHanUserData userData = getUserData(userId);
            if (isAble(userId) && userData.getPositionId() != eTongBingPosition.None.getValue()) {
                ChuHanProto.CrossChuHanCityWinerAppointRespMsg.Builder crespMsg = ChuHanProto.CrossChuHanCityWinerAppointRespMsg.newBuilder();
                crespMsg.setWinerName("");
                crespMsg.setPosition(userData.getPositionId());
                crespMsg.setSendReward(false);
                MessageHelper.sendPacket(userBaseInfo.getServerId(), userId, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_CITY_APPOINT_POSITION, crespMsg));
            }
        }
    }

    //同步变化
    private void syncServerNpcEvent(Map<Long, Map<Long, Map<Integer, Integer>>> serverSyncMap) {
        for (long serverId : serverSyncMap.keySet()) {
            ChuHanProto.CrossChuHanEventSyncMsg.Builder syncMsg = ChuHanProto.CrossChuHanEventSyncMsg.newBuilder();
            Map<Long, Map<Integer, Integer>> userMap = serverSyncMap.get(serverId);
            for (long userId : userMap.keySet()) {
                Map<Integer, Integer> eventMap = userMap.get(userId);
                ChuHanProto.CrossChuHanEventTempMsg.Builder userEventMsg = ChuHanProto.CrossChuHanEventTempMsg.newBuilder();
                userEventMsg.setUserId(userId);
                for (int eventPos : eventMap.keySet()) {
                    ChuHanProto.ChuHanEventMsg.Builder eventMsg = ChuHanProto.ChuHanEventMsg.newBuilder();
                    eventMsg.setPositionNo(eventPos);
                    eventMsg.setEventId(eventMap.get(eventPos));
                    userEventMsg.addEvents(eventMsg);
                }
                syncMsg.addEvents(userEventMsg);
            }
            MessageHelper.sendPacket(serverId, 0, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_NPC_EVENT_SYNC, syncMsg));
        }
    }

    public void syncPatronsByCmd(ChuHanProto.CrossChuHanUpdatePatronsReqMsg reqMsg) {
        Map<Integer, CrossChuHanPatrons> patronsMap = CrossChuHanPb.parseCrossChuHanPatrons(reqMsg.getPatronsList());
        CrossChuHanUserData userData = getUserData(reqMsg.getUserId());
        if (userData == null) {
            return;
        }
        boolean isRefreshMaxSoldier = reqMsg.getIsRefreshMaxSoldier();
        int ret = updatePatronsAbility(userData, false, patronsMap, reqMsg.getSoldierAdditionList(), isRefreshMaxSoldier);

        ChuHanProto.ChuHanUpdatePatronsAbilityRespMsg.Builder respMsg = ChuHanProto.ChuHanUpdatePatronsAbilityRespMsg.newBuilder();
        respMsg.setRet(ret);
        for (CrossChuHanTeam team : userData.getTeamMap().values()) {
            respMsg.addTeam(parseChuHanTeamMsg(userData, team));
        }
        sendPacket(Protocol.S_CHUHAN_UPDATE_PATRONS_ABILITY % 20000, respMsg, reqMsg.getUserId());
    }

    /**
     * 设置战略目标
     */
    public void setTargetCityByCmd(long userId, ChuHanProto.ChuHanSetTargetCityReqMsg reqMsg) {
        int ret = setTargetCity(userId, reqMsg);
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        ChuHanProto.ChuHanSetTargetCityRespMsg.Builder builder = ChuHanProto.ChuHanSetTargetCityRespMsg.newBuilder();
        builder.setRet(ret);
        CrossChuHanTeam team = userData.getTeamMap().get(reqMsg.getOccupation());
        if (team != null) {
            builder.setTeam(parseChuHanTeamMsg(userData, team));
        }
        sendPacket(Protocol.S_CHUHAN_SET_TARGET_CITY % 20000, builder, userId);
    }

    /**
     * 设置战略目标
     */
    public int setTargetCity(long userId, ChuHanProto.ChuHanSetTargetCityReqMsg reqMsg) {
        // 参数校验
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            return GameErrorCode.E_CHUHAN_NO_ABLE;
        }
        int occupation = reqMsg.getOccupation();
        int cityId = reqMsg.getCityId();
        CrossChuHanTeam team = userData.getTeamMap().get(occupation);
        if (team == null) {
            return GameErrorCode.E_CHUHAN_TEAM_NO_EXIST;
        }

        // 时间判断(活动期和休战期可操作)
        eTongBingStage stage = this.config.getTongBingStage();
        if (stage != eTongBingStage.Stage1Time && stage != eTongBingStage.Stage1RestTime) {
            return GameErrorCode.E_CHUHAN_NO_OPERATE_TIME;
        }

        // 判断是否可达
        if (cityId != 0) {
            // 判断是否达到兵力最低要求,不满足的跳过处理
            // 千分比最低兵力和固定最低兵力取最大值
            long minSoldierNum = BigDecimal.valueOf(team.getMaxSoldier())
                    .multiply(BigDecimal.valueOf(config.getTeamNumLowerLimitParam()))
                    .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_UP).longValue();
            if (team.getSoldierNum() < Math.max(minSoldierNum, config.getTeamNumLowerLimitFixedParam())) {
                return GameErrorCode.E_CHUHAN_TEAM_SOLDIER_MIN_LIMIT;
            }

            if (!checkCityCanSetTarget(userData.getCampId(), cityId)) {
                return GameErrorCode.E_CHUHAN_CAN_NOT_SET_TARGET_CITY;
            }
            //楚汉的目标城池不需要路线占领
//            List<Integer> cityRouteList = getRouteList(team.getUserId(), team.getCityId(), cityId);
//            if (cityRouteList == null || cityRouteList.size() <= 1) {
//                return GameErrorCode.E_CHUHAN_NO_ROUTE;
//            }
        }
        // 设置战略目标
        team.setTargetCityId(cityId);
        userData.setUpdateOption();
        return 0;
    }

    public boolean checkCityCanSetTarget(int campId, int cityId) {
        ChuHanCityConfig cityConfig = getConfig().getCityConfigMap().get(cityId);
        if (cityConfig == null) {
            return false;
        }
        if (cityConfig.getType() == eChuHanCityType.Type6_CenterCity.getValue()) {
            //去中心城的时间还没到
            if (System.currentTimeMillis() < config.getStage2BeginTime()) {
                return false;
            }
        } else if (cityConfig.getType() == eChuHanCityType.Type7_BeiJing.getValue() || cityConfig.getType() == eChuHanCityType.Type8_NanYang.getValue()) {
            if (!getConfig().isPveCityOpenTime(cityConfig.getType())) {
                return false;
            }

            if (campId != cityConfig.getMapCamp()) {
                return false;
            }
        }

        return true;
    }

    public void moveTeamByCmd(long userId, ChuHanProto.ChuHanMoveTeamReqMsg reqMsg) {

        int ret = moveToCity(userId, reqMsg);

        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }

        // 第一次发移动请求，都当他引导了
        if (userData.getGuide() == 0) {
            userData.setGuide(1);
            syncBaseDataMsg(userData);
        }

        ChuHanProto.ChuHanMoveTeamRespMsg.Builder builder = ChuHanProto.ChuHanMoveTeamRespMsg.newBuilder();
        builder.setRet(ret);
        for (Integer occu : reqMsg.getOccupationList()) {
            CrossChuHanTeam team = userData.getTeamMap().get(occu);
            if (team != null) {
                builder.addTeam(parseChuHanTeamMsg(userData, team));
            }
        }

        sendPacket(Protocol.S_CHUHAN_MOVE_TEAM % 20000, builder, userId);
    }

    public void speedUpByCmd(long userId, ChuHanProto.ChuHanMoveSpeedUpReqMsg reqMsg) {
        int occupation = reqMsg.getOccupation();
        int times = reqMsg.getTimes();

        int ret = speedUp(userId, occupation, times);
        if (ret != 0) {
            CrossChuHanUserData userData = getUserData(userId);
            CrossChuHanTeam team = userData.getTeamMap().get(occupation);

            ChuHanProto.ChuHanMoveSpeedUpRespMsg.Builder resp = ChuHanProto.ChuHanMoveSpeedUpRespMsg.newBuilder();
            resp.setRet(ret);
            if (team != null) {
                resp.setTeam(parseChuHanTeamMsg(userData, team));
            }

            ChuHanProto.CrossChuHanMoveSpeedUpRespMsg.Builder builder = ChuHanProto.CrossChuHanMoveSpeedUpRespMsg.newBuilder();
            builder.setResp(resp);
            builder.setReqTimes(times);
            sendPacket(GameProtocol.S_CHUHAN_MOVE_SPEED_UP_FROM_CROSS, builder, userId);

            // 发光玩家信息
            syncLight();
        }
    }

    public void getDataByCmd(long userId) {
        ChuHanProto.ChuHanGetDataRespMsg.Builder builder = ChuHanProto.ChuHanGetDataRespMsg.newBuilder();

        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            builder.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
        } else {
            builder.setRet(0);
            for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                builder.addTeam(parseChuHanTeamMsg(userData, team));
            }
            for (CrossChuHanCityData city : getCrossChuHanData().getCityMap().values()) {
                builder.addCity(parseChuHanCityMsg(city));
            }
            builder.setBarracks(parseChuHanBarracksMsg(userId));
            for (CrossChuHanTeam team : getTopTeam()) {
                builder.addTeam(parseChuHanTeamMsg(getUserData(team.getUserId()), team)); // 前几名也加到这边
            }

            List<List<Integer>> effectCampBuffList = getEffectCampBuffList(userData.getCampId());
            for (List<Integer> buffInfos : effectCampBuffList) {
                int buffId = buffInfos.get(0);
                int buffLevel = buffInfos.get(1);
                builder.addBuff(parseChuHanBuffMsg(buffId, buffLevel));
            }
            for (Map.Entry<Integer, Integer> entry : userData.getEventMap().entrySet()) {
                ChuHanProto.ChuHanEventMsg.Builder eventMsg = ChuHanProto.ChuHanEventMsg.newBuilder();
                eventMsg.setPositionNo(entry.getKey());
                eventMsg.setEventId(entry.getValue());
                builder.addNpcEvent(eventMsg);
            }
        }

        //根据访问时间拿PVE战斗结果对应的BUFFID
        parseAndGetPveRewardBuffMsg(builder, userData);

        sendPacket(GameProtocol.S_CHUHAN_GET_DATA_FROM_CROSS, builder, userId);
    }

    private void parseAndGetPveRewardBuffMsg(ChuHanProto.ChuHanGetDataRespMsg.Builder builder, CrossChuHanUserData userData) {
        List<Integer> settleBuffIds = new ArrayList<>();
        if (!getConfig().inEnterActivityTime()) {
            return;
        }
        synchronized (getUserLock(userData.getUserId())) {
            {
                int pveCityType = eChuHanCityType.Type7_BeiJing.getValue();
                if (getConfig().needShowPveResult(pveCityType, userData.getReqPveResultTime())) {
                    CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
                    int pveBuff = campData.getPveBuff(pveCityType);
                    if (pveBuff > 0) {
                        settleBuffIds.add(pveBuff);
                    }
                }
            }

            {
                int pveCityType = eChuHanCityType.Type8_NanYang.getValue();
                if (getConfig().needShowPveResult(pveCityType, userData.getReqPveResultTime())) {
                    CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
                    int pveBuff = campData.getPveBuff(pveCityType);
                    if (pveBuff > 0) {
                        settleBuffIds.add(pveBuff);
                    }
                }
            }
            userData.setReqPveResultTime(DateHelper.getCurrentTime());
        }

        if (!settleBuffIds.isEmpty()) {
            builder.addAllPveBuffId(settleBuffIds);
        }

    }

    /**
     * 比较总战功拿胜方,总战功一样,个人第一在哪哪就是胜方
     *
     * @param cityType
     * @return
     */
    public int getPveWinnerCampId(int cityType) {
        if (!getConfig().isPveCitySettleTime(cityType)) {
            return 0;
        }
        int winnerCampId = 0;
        long winnerZhanGong = 0;
        boolean isSameZhanGong = false;
        for (CrossChuHanCampData campData : campDataMap.values()) {
            int campId = campData.getCampId();
            long pveZhanGong = campData.getPveZhanGong(cityType);
            if (pveZhanGong == 0) {
                continue;
            }

            if (winnerCampId == 0) {
                winnerCampId = campId;
                winnerZhanGong = pveZhanGong;
            } else if (pveZhanGong > winnerZhanGong) {
                winnerCampId = campId;
                winnerZhanGong = pveZhanGong;
            } else if (pveZhanGong == winnerZhanGong) {
                isSameZhanGong = true;
            }
        }

        if (isSameZhanGong) {
            CrossChuHanUserData firstUserData = getPveFirstPveUserData(cityType);
            if (firstUserData != null) {
                winnerCampId = firstUserData.getCampId();
            }
        }
        return winnerCampId;
    }

    private CrossChuHanUserData getPveFirstPveUserData(int cityType) {
        return pveFirstUserDataMap.get(cityType);
    }

    private CrossChuHanUserData getCampPveFirstPveUserData(int campId, int cityType) {
        Map<Integer, CrossChuHanUserData> dataMap = pveCampFirstUserDataMap.get(cityType);
        if (dataMap == null) {
            return null;
        }
        if (dataMap.containsKey(campId)) {
            return dataMap.get(campId);
        }
        return null;
    }

    public void getMyTeamPosByCmd(long userId, ChuHanProto.ChuHanGetBattlefieldMyTeamReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();

        // 返回值
        ChuHanProto.ChuHanGetBattlefieldMyTeamRespMsg.Builder builder = ChuHanProto.ChuHanGetBattlefieldMyTeamRespMsg.newBuilder();
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            builder.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_GET_MY_TEAM_POS % 20000, builder, userId);
            return;
        }
        // 判断资格
        int campId = getUserData(userId).getCampId();

        builder.setRet(0);

        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);
        if (city != null) {
            CrossChuHanBattlefield battlefield = city.getBattlefield();
            if (battlefield != null) {

                // 战场内的
                for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                    LinkedList<CrossChuHanBattlefieldTeam> list = city.getCampId() == campId ? part.getDefList() : part.getAtkList();
                    int no = 0;
                    for (CrossChuHanBattlefieldTeam team : new LinkedList<>(list)) {
                        no++;
                        if (team.getTeam() != null && team.getTeam().getUserId() == userId) {
                            builder.addTeamPos(ChuHanProto.ChuHanBattlefieldMyTeamMsg.newBuilder()
                                    .setPartId(part.getPartId())
                                    .setTeamId(team.getTeam().getTeamId())
                                    .setPos(no));
                        }
                    }
                }

                // 加一下排队中的
                LinkedList<CrossChuHanTeamArrive> arriveLinkedList = city.getCampId() == campId ? city.getDefTeamList() : city.getAtkTeamList();
                int no = 0;
                for (CrossChuHanTeamArrive arrive : new LinkedList<>(arriveLinkedList)) {
                    no++;
                    if (arrive.getUserId() == userId) {
                        builder.addTeamPos(ChuHanProto.ChuHanBattlefieldMyTeamMsg.newBuilder()
                                .setPartId(0)
                                .setTeamId(arrive.getTeamId())
                                .setPos(no));
                    }
                }
            }
        }

        sendPacket(Protocol.S_CHUHAN_GET_MY_TEAM_POS % 20000, builder, userId);
    }

    public void addUserWarReport(CrossChuHanUserWarReport report) {
        synchronized (lock) {
            userWarReportsList.add(report);
            needInsertUserReportList.add(report);
        }

        // 红点
        if (report.getUserIdDef() != 0) {
            addRedDot(getUserData(report.getUserIdDef()), eTongBingRedDotType.Type2_UserWarReport);
        }
        addRedDot(getUserData(report.getUserIdAct()), eTongBingRedDotType.Type2_UserWarReport);
    }

    public void addCityWarReport(CrossChuHanCityWarReport report) {
        synchronized (lock) {
            cityWarReportList.add(report);
            needInsertCityReportList.add(report);
        }

        // 红点
        for (Integer campId : report.getAtkCampId()) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    addRedDot(userData, eTongBingRedDotType.Type3_CityWarReport);
                }
            }
        }
        if (report.getDefCampId() != 0) {
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (userData.getCampId() == report.getDefCampId()) {
                    addRedDot(userData, eTongBingRedDotType.Type3_CityWarReport);
                }
            }
        }
    }

    //获得城池战报
    public void cityWarReport(long userId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        ChuHanProto.ChuHanCityWarReportRespMsg.Builder respMsg = ChuHanProto.ChuHanCityWarReportRespMsg.newBuilder();
        respMsg.setRet(0);
        List<CrossChuHanCityWarReport> list = new ArrayList<>(cityWarReportList);
        int count = 0;
        for (int i = list.size() - 1; i >= 0; i--) {
            CrossChuHanCityWarReport report = list.get(i);
            if (count >= config.getCityReportNum()) {
                break;
            }
            if (report.getDefCampId() == userData.getCampId() || report.getAtkCampId().contains(userData.getCampId())) {
                count++;
                ChuHanProto.ChuHanCityWarReportMsg.Builder tempMsg = ChuHanProto.ChuHanCityWarReportMsg.newBuilder();
                for (int atkCampId : report.getAtkCampId()) {
                    tempMsg.addAtkCampId(atkCampId);
                }
                tempMsg.setDefCampId(report.getDefCampId());
                tempMsg.setType(report.getType());
                tempMsg.setCityId(report.getCityId());
                tempMsg.setTime(report.getTime());
                for (CrossChuHanBattlefieldRank atkRank : report.getAtkRankList()) {
                    tempMsg.addAtkRank(parseChuHanBattlefieldRankMsg(atkRank));
                }
                for (CrossChuHanBattlefieldRank defRank : report.getDefRankList()) {
                    tempMsg.addDefRank(parseChuHanBattlefieldRankMsg(defRank));
                }
                tempMsg.setWinCampId(report.getWinCampId());
                respMsg.addList(tempMsg);
            }
        }
        sendPacket(Protocol.S_CHUHAN_CITY_WAR_REPORT % 20000, respMsg, userId);
    }

    //获得个人战报
    public void userWarReport(long userId) {
        ChuHanProto.ChuHanUserWarReportRespMsg.Builder respMsg = ChuHanProto.ChuHanUserWarReportRespMsg.newBuilder();
        respMsg.setRet(0);
        List<CrossChuHanUserWarReport> list = new ArrayList<>(userWarReportsList);
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        String myNickName = "";
        if (userBaseInfo != null) {
            myNickName = userBaseInfo.getNickName();
        }
        int count = 0;
        for (int i = list.size() - 1; i >= 0; i--) {
            CrossChuHanUserWarReport report = list.get(i);
            if (count >= config.getUserReportNum()) {
                break;
            }
            if (report.getUserIdAct() == userId) {
                count++;
                ChuHanProto.ChuHanUserWarReportMsg.Builder tempMsg = ChuHanProto.ChuHanUserWarReportMsg.newBuilder();
                tempMsg.setNo(report.getNo());
                tempMsg.setMyPatronsId(report.getActPatronsId());
                tempMsg.setMySoldierNum(report.getActSoldierNum());
                tempMsg.setOtherPatronsId(report.getDefPatronsId());
                tempMsg.setOtherSoldierNum(report.getDefSoldierNum());
                tempMsg.setTime(report.getTime());
                tempMsg.setAddZhanGong(report.getActZhanGong());
                tempMsg.setKillSoldierNum(report.getDefDieSoldierNum());
                tempMsg.setCityId(report.getCityId());
                tempMsg.setIsWin(report.getWinUserId() == 2);
                tempMsg.setCostSoldierNum(report.getActDieSoldierNum());
                tempMsg.setMyAddition(report.getActAddition());
                tempMsg.setOtherAddition(report.getDefAddition());
                tempMsg.setMyNickName(myNickName);
                String otherNickName = "";
                if (report.getUserIdDef() != 0) {
                    UserBaseInfo otherUserBaseInfo = CrossUserMgr.getUserBaseInfo(report.getUserIdDef());
                    if (otherUserBaseInfo != null) {
                        otherNickName = otherUserBaseInfo.getNickName();
                    }
                }
                tempMsg.setOtherNickName(otherNickName);
                respMsg.addList(tempMsg);
            } else if (report.getUserIdDef() == userId) {
                count++;
                ChuHanProto.ChuHanUserWarReportMsg.Builder tempMsg = ChuHanProto.ChuHanUserWarReportMsg.newBuilder();
                tempMsg.setNo(report.getNo());
                tempMsg.setMyPatronsId(report.getDefPatronsId());
                tempMsg.setMySoldierNum(report.getDefSoldierNum());
                tempMsg.setOtherPatronsId(report.getActPatronsId());
                tempMsg.setOtherSoldierNum(report.getActSoldierNum());
                tempMsg.setTime(report.getTime());
                tempMsg.setAddZhanGong(report.getDefZhanGong());
                tempMsg.setKillSoldierNum(report.getActDieSoldierNum());
                tempMsg.setCityId(report.getCityId());
                tempMsg.setIsWin(report.getWinUserId() != 2);
                tempMsg.setCostSoldierNum(report.getDefDieSoldierNum());
                tempMsg.setMyAddition(report.getDefAddition());
                tempMsg.setOtherAddition(report.getActAddition());
                tempMsg.setMyNickName(myNickName);
                String otherNickName = "";
                if (report.getUserIdAct() != 0) {
                    UserBaseInfo otherUserBaseInfo = CrossUserMgr.getUserBaseInfo(report.getUserIdAct());
                    if (otherUserBaseInfo != null) {
                        otherNickName = otherUserBaseInfo.getNickName();
                    }
                }
                tempMsg.setOtherNickName(otherNickName);
                respMsg.addList(tempMsg);
            }
        }
        sendPacket(Protocol.S_CHUHAN_USER_WAR_REPORT % 20000, respMsg, userId);
    }

    //活动阶段2城主等信息
    public void getCityWinner(long userId) {
        ChuHanProto.ChuHanCityWinerRespMsg.Builder respMsg = ChuHanProto.ChuHanCityWinerRespMsg.newBuilder();
        respMsg.setRet(0);
        long winnerUserId = crossChuHanData.getProcess2WinUserId();
        for (CrossChuHanUserData userData : userDataMap.values()) {

            boolean needSync = userData.getPositionId() != 0; // 有职位，必须发
            if (userId == winnerUserId) { // 自己是城主，就要所有人的信息
                needSync = true;
            }

            if (!needSync) {
                continue;
            }

            UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userData.getUserId());
            if (userBaseInfo != null) {
                ChuHanProto.ChuHanUserPositionMsg.Builder userTempMsg = ChuHanProto.ChuHanUserPositionMsg.newBuilder();
                userTempMsg.setUserId(userData.getUserId());
                userTempMsg.setCampId(userData.getCampId());
                userTempMsg.setZhangong(userData.getZhanGong());
                userTempMsg.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
                userTempMsg.setTotalPosSilver(userData.getTotalPosSilver().toString());
                if (winnerUserId == userData.getUserId()) {
                    userTempMsg.setPosition(eTongBingPosition.Master.getValue());
                } else {
                    userTempMsg.setPosition(userData.getPositionId());
                }
                respMsg.addList(userTempMsg);
            }
        }
        sendPacket(Protocol.S_CHUHAN_CITY_WINNER % 20000, respMsg, userId);
    }

    //获取玩家的赚速，任命职位的时候扣别人的赚速加到自己身上就是用这个赚速
    private BigInteger getUserEarnSpeed(long userId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return BigInteger.ZERO;
        }
        if (unionJoinMap.containsKey(userData.getUnionUid())) {
            Map<Long, CrossChuHanUnionJoinMember> joinMemberMap = unionJoinMap.get(userData.getUnionUid()).getMemberMap();
            if (joinMemberMap.containsKey(userId)) {
                return BigInteger.valueOf(joinMemberMap.get(userId).getEarnSpeed());
            }
        }
        return BigInteger.ZERO;
    }

    public void cityWinnerAppointPosition(long userId, ChuHanProto.CrossChuHanCityWinerAppointReqMsg reqMsg) {
        ChuHanProto.ChuHanCityWinerRespMsg.Builder respMsg = ChuHanProto.ChuHanCityWinerRespMsg.newBuilder();
        respMsg.setRet(0);
        long winnerUserId = crossChuHanData.getProcess2WinUserId();
        if (userId != winnerUserId) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_APPOINT_NOT_WINNER);
            sendPacket(Protocol.S_CHUHAN_CITY_WINNER_APPOINT % 20000, respMsg, userId);
            return;
        }
        CrossChuHanUserData winnerUserData = getUserData(winnerUserId);
        if (winnerUserData == null) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_CITY_WINNER_APPOINT % 20000, respMsg, userId);
            return;
        }
        CrossChuHanUserData userData = getUserData(reqMsg.getUserId());//任命的玩家
        if (userData == null) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_CITY_WINNER_APPOINT % 20000, respMsg, userId);
            return;
        }
        synchronized (lock) {
            Map<Integer, ChuHanTitleConfig> titleConfigMap = config.getTitleConfigMap();
            if (!titleConfigMap.containsKey(reqMsg.getPosition())) {
                respMsg.setRet(GameErrorCode.E_CHUHAN_USER_POSITION_APPOINT_ERR);
                sendPacket(Protocol.S_CHUHAN_CITY_WINNER_APPOINT % 20000, respMsg, userId);
                return;
            }
            ChuHanTitleConfig titleConfig = titleConfigMap.get(reqMsg.getPosition());
            if (userData.getPositionId() != eTongBingPosition.None.getValue()) {
                respMsg.setRet(GameErrorCode.E_CHUHAN_USER_POSITION_APPOINT);
                sendPacket(Protocol.S_CHUHAN_CITY_WINNER_APPOINT % 20000, respMsg, userId);
                return;
            }
            //任命的时候校验下是不是这个职位已经任命过了
            for (CrossChuHanUserData userDd : new ArrayList<>(userDataMap.values())) {
                if (userDd.getPositionId() == reqMsg.getPosition()) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_USER_POSITION_APPOINT_ERR);
                    sendPacket(Protocol.S_CHUHAN_CITY_WINNER_APPOINT % 20000, respMsg, userId);
                    return;
                }
            }
            //自己阵营只能buff，别人阵营的只能debuff
            if (winnerUserData.getCampId() == userData.getCampId()) {
                if (titleConfig.getType() != 1) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_USER_APPOINT_ERR_POSITION);
                    sendPacket(Protocol.S_CHUHAN_CITY_WINNER_APPOINT % 20000, respMsg, userId);
                    return;
                }
            } else {
                if (titleConfig.getType() != 2) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_USER_APPOINT_ERR_POSITION);
                    sendPacket(Protocol.S_CHUHAN_CITY_WINNER_APPOINT % 20000, respMsg, userId);
                    return;
                }
            }
            userData.setPositionId(reqMsg.getPosition());
            //任命开始算钱
            userData.setPosAddSilverTime(System.currentTimeMillis());
            if (titleConfig.getType() == 1) {
                addNeedSyncPosUser(userData);
            }

            // 日志
            CrossAutoLogMgr.add(new LogChuHanAppoint(
                    config.getActivityInfo().getActivityId(),
                    userId,
                    reqMsg.getUserId(),
                    titleConfig.getId(),
                    groupId,
                    winnerUserData.getCampId()
            ));

            //减益buff还要去本服通知扣除他英两的增加
            UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userData.getUserId());
            UserBaseInfo winnerUserBaseInfo = CrossUserMgr.getUserBaseInfo(winnerUserId);
            if (userBaseInfo != null) {
                ChuHanProto.CrossChuHanCityWinerAppointRespMsg.Builder crespMsg = ChuHanProto.CrossChuHanCityWinerAppointRespMsg.newBuilder();
                crespMsg.setWinerName(winnerUserBaseInfo == null ? "" : winnerUserBaseInfo.getNickName());
                crespMsg.setPosition(reqMsg.getPosition());
                crespMsg.setSendReward(true);
                MessageHelper.sendPacket(userBaseInfo.getServerId(), userData.getUserId(), YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_CITY_APPOINT_POSITION, crespMsg));
            } else {
                logger.error("任命找不到任命玩家的userBaseInfo");
            }
            sendPacket(Protocol.S_CHUHAN_CITY_WINNER_APPOINT % 20000, respMsg, userId);
        }
    }

    /**
     * 任命指挥官
     */
    public void campCommanderAppoint(long userId, ChuHanProto.CrossChuHanCampCommanderAppointReqMsg reqMsg) {
        ChuHanProto.ChuHanCampCommanderAppointRespMsg.Builder respMsg = ChuHanProto.ChuHanCampCommanderAppointRespMsg.newBuilder();
        respMsg.setRet(0);
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_CAMP_COMMANDER_APPOINT % 20000, respMsg, userId);
            return;
        }
        long maxZhanGongUserId = getCampMaxZhanGongUserId(userData.getCampId());
        if (userId != maxZhanGongUserId || userData.getZhanGong() < config.getAppointScoreLimit()) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_CAN_NOT_SET_COMMANDER);
            sendPacket(Protocol.S_CHUHAN_CAMP_COMMANDER_APPOINT % 20000, respMsg, userId);
            return;
        }
        CrossChuHanUserData appointUserData = getUserData(reqMsg.getUserId());//任命的玩家
        if (appointUserData == null) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_CAMP_COMMANDER_APPOINT % 20000, respMsg, userId);
            return;
        }
        synchronized (lock) {
            // 只能任命当前阵营的人
            if (appointUserData.getCampId() != userData.getCampId()) {
                respMsg.setRet(GameErrorCode.E_CHUHAN_COMMANDER_ONLY_SET_THE_SAME_CAMP);
                sendPacket(Protocol.S_CHUHAN_CAMP_COMMANDER_APPOINT % 20000, respMsg, userId);
                return;
            }
            CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
            CrossChuHanCommanderData commanderData = campData.getCommanderData();
            if (commanderData == null) {
                commanderData = new CrossChuHanCommanderData();
            }
            commanderData.setUserId(appointUserData.getUserId());
            campData.setCommanderData(commanderData);
            sendPacket(Protocol.S_CHUHAN_CAMP_COMMANDER_APPOINT % 20000, respMsg, userId);
            // 同步信息给阵营玩家
            syncCommanderInfoToCamp(userData.getCampId());
        }
    }

    /**
     * 指挥官设置信息
     */
    public void commanderSet(long userId, ChuHanProto.CrossChuHanCommanderSetReqMsg reqMsg) {
        ChuHanProto.ChuHanCommanderSetRespMsg.Builder respMsg = ChuHanProto.ChuHanCommanderSetRespMsg.newBuilder();
        respMsg.setRet(0);
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        // 判断是否为指挥官
        synchronized (lock) {
            CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
            CrossChuHanCommanderData commanderData = campData.getCommanderData();
            if (commanderData == null) {
                commanderData = new CrossChuHanCommanderData();
            }
            long maxZhanGongUserId = getCampMaxZhanGongUserId(userData.getCampId());
            // 只有战功第一的或者指挥官才可设置
            boolean isMaxZhangong = maxZhanGongUserId == userId && userData.getZhanGong() >= config.getAppointScoreLimit();
            boolean isCommander = commanderData.getUserId() == userId;
            if (!isMaxZhangong && !isCommander) {
                respMsg.setRet(GameErrorCode.E_CHUHAN_NOT_COMMANDER);
                sendPacket(Protocol.S_CHUHAN_COMMANDER_SET % 20000, respMsg, userId);
                return;
            }
            // 设置信息
            boolean isNeedSync = false;
            ChuHanProto.ChuHanCommanderSetReqMsg commanderSetReqMsg = reqMsg.getCommanderSetReqMsg();
            if (commanderSetReqMsg.hasNotice()) {
                commanderData.setNotice(commanderSetReqMsg.getNotice());
                isNeedSync = true;
            }
            if (commanderSetReqMsg.hasMajorCityId()) {
                commanderData.setMajorCityId(commanderSetReqMsg.getMajorCityId());
                isNeedSync = true;
            }
            if (commanderSetReqMsg.hasMinorCityId()) {
                commanderData.setMinorCityId(commanderSetReqMsg.getMinorCityId());
                isNeedSync = true;
            }
            sendPacket(Protocol.S_CHUHAN_COMMANDER_SET % 20000, respMsg, userId);
            if (isNeedSync) {
                campData.setCommanderData(commanderData);
                // 同步信息给阵营玩家
                syncCommanderInfoToCamp(userData.getCampId());
            }
        }
    }

    /**
     * 获取同阵营的用户数据
     */
    public void getCampUserData(long userId, ChuHanProto.CrossChuHanCampUserDataReqMsg reqMsg) {
        ChuHanProto.ChuHanCampUserDataRespMsg.Builder respMsg = ChuHanProto.ChuHanCampUserDataRespMsg.newBuilder();
        respMsg.setRet(0);
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_CAMP_USER_DATA % 20000, respMsg, userId);
            return;
        }
        long maxZhanGongUserId = getCampMaxZhanGongUserId(userData.getCampId());
        for (CrossChuHanUserData tongBingUserData : userDataMap.values()) {
            // 获取阵营
            if (tongBingUserData.getCampId() == userData.getCampId()) {
                UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(tongBingUserData.getUserId());
                if (userBaseInfo != null) {
                    ChuHanProto.ChuHanCampUserDataTemp.Builder temp = ChuHanProto.ChuHanCampUserDataTemp.newBuilder();
                    temp.setUserId(tongBingUserData.getUserId());
                    temp.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
                    temp.setZhangong(tongBingUserData.getZhanGong());
                    temp.setIsMaxZhanGong(tongBingUserData.getUserId() == maxZhanGongUserId);
                    respMsg.addCampUserData(temp);
                }
            }
        }
        sendPacket(Protocol.S_CHUHAN_CAMP_USER_DATA % 20000, respMsg, userId);
    }


    /**
     * 添加伤兵
     */
    public void addBarracksWoundedSoldier(long userId, long num) {
        barracksCalc(userId);
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }

        String buffString = parseCampBuffString(userData.getCampId());

        synchronized (lock) {
            CrossChuHanBarracks barracks = userData.getBarracks();

            // 日志用
            String logBeforeBarracks = JSON.toJSONString(barracks);

            long woundedNum = barracks.getWoundedNum();
            long woundedMaxNum = barracks.getWoundedMaxNum();
            if (woundedNum >= woundedMaxNum) {
                // 到达上限，不操作
            } else if (woundedNum + num >= woundedMaxNum) {
                // 加完后超过上限，设置为上限
                barracks.setWoundedNum(woundedMaxNum);
            } else {
                // 正常添加
                barracks.setWoundedNum(woundedNum + num);
            }
            barracks.setRescueSpeed(config.getWoundedRescueTime());
            barracks.setRescueCount(getBarracksWoundedCount(userId));
            if (barracks.getRescueTime() == 0) {
                barracks.setRescueTime(System.currentTimeMillis());
            }
            userData.setUpdateOption();

            // 日志
            CrossAutoLogMgr.add(new LogChuHanSoldierChange(
                    userData.getActivityId(),
                    userData.getUserId(),
                    userData.getGroupId(),
                    userData.getCampId(),
                    5,
                    num,
                    -1,
                    logBeforeBarracks,
                    JSON.toJSONString(barracks),
                    buffString
                    ));
        }
    }

    private String parseCampBuffString(int campId) {
        List<List<Integer>> effectCampBuffList = getEffectCampBuffList(campId);
        String campBuffStr = "";
        for (List<Integer> buffInfos : effectCampBuffList) {
            int buffId = buffInfos.get(0);
            int buffLevel = buffInfos.get(1);
            String buffStr = buffId + "=" + buffLevel;
            campBuffStr += buffStr + ",";
        }
        return campBuffStr;
    }

    public void getUserBarracksInfo(long userId) {
        barracksCalc(userId);
        ChuHanProto.ChuHanGetBarracksRespMsg.Builder respMsg = ChuHanProto.ChuHanGetBarracksRespMsg.newBuilder();
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_GET_BARRACKS % 20000, respMsg, userId);
            return;
        }
        respMsg.setRet(0);
        respMsg.setBarracks(parseChuHanBarracksMsg(userId));
        sendPacket(Protocol.S_CHUHAN_GET_BARRACKS % 20000, respMsg, userId);
    }

    /**
     * 计算兵营的产出
     */
    private void barracksCalc(long userId) {
        long nowTime = System.currentTimeMillis();
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        CrossChuHanBarracks barracks = userData.getBarracks();

        synchronized (lock) {
            if (barracks.getCreateNum() > 0) {
                long createTime = barracks.getCreateTime();
                // 征兵了几秒
                long second = (nowTime - createTime) / DateHelper.SECOND_MILLIONS;
                // 征兵了几个
                long createNumber = second * barracks.getCreateSpeed();
                // 可以全部征完
                if (createNumber >= barracks.getCreateNum()) {
                    createNumber = barracks.getCreateNum();
                }

                barracks.setCreateNum(barracks.getCreateNum() - createNumber);
                if (barracks.getCreateNum() == 0) {
                    barracks.setCreateTime(0);
                    //全部征兵完了设置一下速度和花费和数量为0
                    barracks.setCreateSpeed(0);
                    barracks.setCreateCost(0);
                    barracks.setCreateCount(0);
                } else {
                    barracks.setCreateTime(barracks.getCreateTime() + second * DateHelper.SECOND_MILLIONS);
                }
                barracks.setReadyNum(barracks.getReadyNum() + createNumber);
                userData.setUpdateOption();
            }

            if (barracks.getWoundedNum() > 0) {
                long rescueTime = barracks.getRescueTime();
                // 修复次数
                long rescueNumber = (nowTime - rescueTime) / barracks.getRescueSpeed() * barracks.getRescueCount();
                // 时间到了全部伤兵时间
                if (rescueNumber >= barracks.getWoundedNum()) {
                    rescueNumber = barracks.getWoundedNum();
                }
                barracks.setWoundedNum(barracks.getWoundedNum() - rescueNumber);
                if (barracks.getWoundedNum() == 0) {
                    barracks.setRescueTime(0);
                } else {
                    barracks.setRescueTime(barracks.getRescueTime() + (rescueNumber / barracks.getRescueCount()) * barracks.getRescueSpeed());
                }
                barracks.setRescueNum(barracks.getRescueNum() + rescueNumber);
                userData.setUpdateOption();
            }
        }
    }

    /**
     * 兵营玩家道具操作
     *
     * @param type 1扣消耗，2返回预扣除，3不操作，4加道具
     */
    private void sendBarracksOpMsg(long serverId, long userId, int type, String cost, ChuHanProto.ChuHanRecruitSoldierRespMsg respMsg) {
        ChuHanProto.CrossChuHanBarracksOpMsg.Builder builder = ChuHanProto.CrossChuHanBarracksOpMsg.newBuilder();
        builder.setActivityId(config.getActivityInfo().getActivityId());
        builder.setType(type);
        builder.setCost(cost);
        if (respMsg != null) {
            builder.setResMsg(respMsg);
        }
        MessageHelper.sendPacket(serverId, userId, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_BARRACKS_OP, builder));
    }

    /**
     * 获取招募所需银两
     */
    public void recruitSoldierSliver(long serverId, long userId, ChuHanProto.CrossChuHanRecruitSoldierReqMsg reqMsg) {
        long speed = reqMsg.getSpeed();
        long num = reqMsg.getNum();

        Map<Long, Long> recruitSoldierMap = config.getRecruitSoldierMap();
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        CrossChuHanBarracks barracks = userData.getBarracks();

        ChuHanProto.ChuHanRecruitSoldierRespMsg.Builder respMsg = ChuHanProto.ChuHanRecruitSoldierRespMsg.newBuilder();

        // 没有找到对应速度的招募
        if (!recruitSoldierMap.containsKey(speed)) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_BARRACKS_SPEED);
            sendPacket(Protocol.S_CHUHAN_RECRUIT_SOLDIER % 20000, respMsg, userId);
            return;
        }

        // 上一次的招募还没有结束
        barracksCalc(userId);
        if (barracks.getCreateNum() != 0) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_BARRACKS_ING);
            sendPacket(Protocol.S_CHUHAN_RECRUIT_SOLDIER % 20000, respMsg, userId);
            return;
        }

        // 超出招募上限
        if (num + barracks.getReadyNum() > barracks.getReadyMaxNum()) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_BARRACKS_FULL);
            sendPacket(Protocol.S_CHUHAN_RECRUIT_SOLDIER % 20000, respMsg, userId);
            return;
        }

        // 每秒银两
        BigInteger silver = calcRecruitPerSecondSilver(userId, speed);

        // 每个兵多少钱
        BigInteger silverPerSoldier = new BigDecimal(silver).divide(BigDecimal.valueOf(speed), 0, BigDecimal.ROUND_UP).toBigInteger();

        // 总银两  = 士兵数 * 总银两
        BigInteger totalSilver = silverPerSoldier.multiply(BigInteger.valueOf(num));

        ChuHanProto.CrossChuHanRecruitSoldierReqMsg.Builder respServerMsg = reqMsg.toBuilder();
        respServerMsg.setSilver(totalSilver.toString());
        MessageHelper.sendPacket(serverId, userId, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_CALL_SOLDIER_SLIVER_RESP, respServerMsg));
    }

    /**
     * 计算招募每秒消耗银两（含BUFF）
     */
    private BigInteger calcRecruitPerSecondSilver(long userId, long speed) {
        // 拿配置
        Map<Long, Long> recruitSoldierMap = config.getRecruitSoldierMap();
        long silver = recruitSoldierMap.get(speed);
        // 加buff
        Integer buffAddition = getBuffAddition(userId, eChuHanBuffType.Type2_RecruitSoldierPrice).get(0);
        // 计算结果
        BigInteger result = BigDecimal.valueOf(silver)
                .multiply(BigDecimal.valueOf(1000 - buffAddition))
                .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_UP).toBigInteger();
        // 容错
        if (result.compareTo(BigInteger.ONE) <= 0) {
            result = BigInteger.ONE;
        }
        return result;
    }

    /**
     * 招募士兵
     */
    public void recruitSoldier(long serverId, long userId, ChuHanProto.CrossChuHanRecruitSoldierReqMsg reqMsg) {

        barracksCalc(userId);
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        CrossChuHanBarracks barracks = userData.getBarracks();

        ChuHanProto.ChuHanRecruitSoldierRespMsg.Builder respMsg = ChuHanProto.ChuHanRecruitSoldierRespMsg.newBuilder();
        respMsg.setRet(0);

        int type = reqMsg.getType();
        long speed = reqMsg.getSpeed();
        long num = reqMsg.getNum(); // 士兵数量 or 兵符数量

        String costStr = "";
        String campBuffStr = parseCampBuffString(userData.getCampId());

        synchronized (lock) {

            // 日志用
            String logBeforeBarracks = JSON.toJSONString(barracks);

            // 【取消银两招兵】
            if (type == 1 && speed == 0) {
                if (barracks.getCreateNum() <= 0) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_BARRACKS_NO_NEED_CANCEL);
                    sendBarracksOpMsg(serverId, userId, 3, costStr, respMsg.build());
                    return;
                }

                // 剩余银两 = 每秒银两 x ( 剩余士兵数 / 每秒士兵数 )
                long sliver = barracks.getCreateCost();
                BigInteger cost = BigDecimal.valueOf(sliver).multiply(
                        BigDecimal.valueOf(barracks.getCreateNum()).divide(BigDecimal.valueOf(barracks.getCreateSpeed()), 8, BigDecimal.ROUND_DOWN)
                ).setScale(0, BigDecimal.ROUND_DOWN).toBigInteger();

                // 设置返还银两
                costStr = GameConfig.GAME_MONEY_SILVER + "=" + cost.toString();

                // 修改兵营
                barracks.setCreateCost(0);
                barracks.setCreateCount(0);
                barracks.setCreateSpeed(0);
                barracks.setCreateTime(0);
                barracks.setCreateNum(0);
                userData.setUpdateOption();

                // 返回
                respMsg.setBarracks(parseChuHanBarracksMsg(userId));
                sendBarracksOpMsg(serverId, userId, 4, costStr, respMsg.build());

                // 日志
                CrossAutoLogMgr.add(new LogChuHanSoldierChange(
                        userData.getActivityId(),
                        userData.getUserId(),
                        userData.getGroupId(),
                        userData.getCampId(),
                        4,
                        num,
                        -1,
                        logBeforeBarracks,
                        JSON.toJSONString(barracks),
                        campBuffStr
                ));
                return;
            }


            if (type == 1 && speed != 0) {
                // 【银两募兵】

                // 没有找到对应速度的招募
                Map<Long, Long> recruitSoldierMap = config.getRecruitSoldierMap();
                if (!recruitSoldierMap.containsKey(speed)) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_BARRACKS_SPEED);
                    sendBarracksOpMsg(serverId, userId, 2, costStr, respMsg.build());
                    return;
                }

                // 上一次的招募还没有结束
                if (barracks.getCreateNum() != 0) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_BARRACKS_ING);
                    sendBarracksOpMsg(serverId, userId, 2, costStr, respMsg.build());
                    return;
                }

                // 超上限
                if (num + barracks.getReadyNum() > barracks.getReadyMaxNum()) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_BARRACKS_FULL);
                    sendBarracksOpMsg(serverId, userId, 2, costStr, respMsg.build());
                    return;
                }

                // 设置消耗
                costStr = GameConfig.GAME_MONEY_SILVER + "=" + reqMsg.getSilver();

                // 每个兵的消耗
                BigInteger soldierSilver = new BigDecimal(reqMsg.getSilver()).divide(BigDecimal.valueOf(num), 0, BigDecimal.ROUND_UP).toBigInteger();
                // 每秒消耗
                BigInteger secondSilver = soldierSilver.multiply(BigInteger.valueOf(speed));

                // 改数据
                barracks.setCreateNum(num);
                barracks.setCreateTime(System.currentTimeMillis());
                barracks.setCreateSpeed(speed);
                barracks.setCreateCost(secondSilver.longValue());
                barracks.setCreateCount(1);
                userData.setUpdateOption();

            } else if (type == 2) {
                // 【伤兵救治】
                if (barracks.getRescueNum() < num) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_BARRACKS_HOSPITAL_NO_ENOUGH);
                    sendBarracksOpMsg(serverId, userId, 3, costStr, respMsg.build());
                    return;
                }
                barracks.setReadyNum(barracks.getReadyNum() + num);
                barracks.setRescueNum(barracks.getRescueNum() - num);
                userData.setUpdateOption();

            } else if (type == 3) {
                // 【兵符募兵】

                // 设置消耗
                costStr = config.getBingFuItem() + "=" + num;

                barracks.setReadyNum(barracks.getReadyNum() + num * reqMsg.getBingFuNum());
                userData.setUpdateOption();

            } else {
                // 【参数错误】

                respMsg.setRet(GameErrorCode.E_CHUHAN_PARAM_ERR);
                sendBarracksOpMsg(serverId, userId, 2, costStr, respMsg.build());
                return;
            } // 【加类型下面日志要改】

            respMsg.setBarracks(parseChuHanBarracksMsg(userId));
            sendBarracksOpMsg(serverId, userId, 1, costStr, respMsg.build());

            // 日志
            CrossAutoLogMgr.add(new LogChuHanSoldierChange(
                    userData.getActivityId(),
                    userData.getUserId(),
                    userData.getGroupId(),
                    userData.getCampId(),
                    type, // 123刚好是对应的
                    num,
                    type == 1 ? speed : -1,
                    logBeforeBarracks,
                    JSON.toJSONString(barracks),
                    campBuffStr
            ));
        }
    }

    public void changeSoldier(long userId, ChuHanProto.CrossChuHanChangeTeamSoldierReqMsg reqMsg) {
        ChuHanProto.ChuHanChangeTeamSoldierRespMsg.Builder respMsg = ChuHanProto.ChuHanChangeTeamSoldierRespMsg.newBuilder();
        respMsg.setRet(0);
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_CHANGE_TEAM_SOLDIER % 20000, respMsg, userId);
            return;
        }

        barracksCalc(userId);

        Map<Integer, CrossChuHanTeam> teamMap = userData.getTeamMap();
        long allValue = 0L;
        String campBuffStr = parseCampBuffString(userData.getCampId());
        synchronized (lock) {

            // 日志用
            String logBeforeBarracks = JSON.toJSONString(userData.getBarracks());

            for (ChuHanProto.ChuHanChangeTeamSoldierMsg changeMsg : reqMsg.getChangeList()) {
                int occType = changeMsg.getOccupation();
                CrossChuHanTeam team = teamMap.get(occType);
                if (getTeamStatus(team) != eTongBingTeamStatus.Free) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_TEAM_CHANGE_SOLDIER_NO_FREE);
                    sendPacket(Protocol.S_CHUHAN_CHANGE_TEAM_SOLDIER % 20000, respMsg, userId);
                    return;
                }
                if (changeMsg.getSoldierNum() > team.getMaxSoldier() || changeMsg.getSoldierNum() < 0) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_TEAM_CHANGE_SOLDIER_MAX);
                    sendPacket(Protocol.S_CHUHAN_CHANGE_TEAM_SOLDIER % 20000, respMsg, userId);
                    return;
                }
                allValue = allValue + (changeMsg.getSoldierNum() - team.getSoldierNum());
            }
            if (allValue > userData.getBarracks().getReadyNum()) {
                respMsg.setRet(GameErrorCode.E_CHUHAN_TEAM_CHANGE_SOLDIER_NO_ENOUGH);
                sendPacket(Protocol.S_CHUHAN_CHANGE_TEAM_SOLDIER % 20000, respMsg, userId);
                return;
            }
            for (ChuHanProto.ChuHanChangeTeamSoldierMsg changeMsg : reqMsg.getChangeList()) {
                int occType = changeMsg.getOccupation();
                CrossChuHanTeam team = teamMap.get(occType);
                team.setSoldierNum(changeMsg.getSoldierNum());
            }
            userData.setUpdateOption();

            for (CrossChuHanTeam team : teamMap.values()) {
                respMsg.addTeam(parseChuHanTeamMsg(userData, team));
            }
            CrossChuHanBarracks barracks = userData.getBarracks();
            barracks.setReadyNum(userData.getBarracks().getReadyNum() - allValue);
            userData.setUpdateOption();
            respMsg.setBarracks(parseChuHanBarracksMsg(userId));
            sendPacket(Protocol.S_CHUHAN_CHANGE_TEAM_SOLDIER % 20000, respMsg, userId);

            // 日志
            CrossAutoLogMgr.add(new LogChuHanSoldierChange(
                    userData.getActivityId(),
                    userData.getUserId(),
                    userData.getGroupId(),
                    userData.getCampId(),
                    6,
                    allValue,
                    -1,
                    logBeforeBarracks,
                    JSON.toJSONString(barracks),
                    campBuffStr
            ));
        }
    }

    /**
     * 楚汉不要这个
     */
    public void makeNpcEvent() {
        long npcEventAddTime = getCrossChuHanData().getNpcEventAddTime();
        long nowTime = System.currentTimeMillis();
        long startTime = DateHelper.getTodayZeroTimeStamp() + config.getNpcEventBeginTime();

        // 今天未开始 || 小于上次生成时间 || 活动结束了
        if (nowTime < startTime || nowTime < npcEventAddTime || nowTime > config.getActivityInfo().getEndTime() * 1000L) {
            return;
        }

        for (CrossChuHanUserData userData : userDataMap.values()) {
            if (userData.getEventResetTime() != DateHelper.getTodayZeroTimeStamp() + config.getNpcEventBeginTime()) {
                userData.setEventResetTime(DateHelper.getTodayZeroTimeStamp() + config.getNpcEventBeginTime());
                userData.setEventTodayCount(0);
            }
        }

        Map<Long, Map<Long, Map<Integer, Integer>>> serverSyncMap = new ConcurrentHashMap<>();
        if (nowTime >= npcEventAddTime + config.getNpcEventInterval()) {
            long times = (nowTime - npcEventAddTime) / config.getNpcEventInterval();
            npcEventAddTime = npcEventAddTime + times * config.getNpcEventInterval();
            for (int i = 0; i < times; i++) {
                for (CrossChuHanUserData userData : userDataMap.values()) {
                    Map<Integer, Integer> userEventMap = userData.getEventMap();
                    if (userEventMap.size() < config.getNpcEventMaxNum() && userData.getEventTodayCount() < config.getNpcEventDayMaxNum()) {
                        int eventPos = randomNpcEventPosition(userEventMap);
                        if (eventPos != -1) {
                            ChuHanNpcEventConfig npcEventConfig = randomNpcEvent();
                            userEventMap.put(eventPos, npcEventConfig.getId());
                            userData.setUpdateOption();
                            userData.setEventTodayCount(userData.getEventTodayCount() + 1);
                            UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userData.getUserId());
                            if (userBaseInfo != null) {
                                if (!serverSyncMap.containsKey(userBaseInfo.getServerId())) {
                                    serverSyncMap.put(userBaseInfo.getServerId(), new ConcurrentHashMap<>());
                                }
                                Map<Long, Map<Integer, Integer>> userSyncMap = serverSyncMap.get(userBaseInfo.getServerId());
                                if (!userSyncMap.containsKey(userData.getUserId())) {
                                    userSyncMap.put(userData.getUserId(), new ConcurrentHashMap<>());
                                }
                                userSyncMap.get(userData.getUserId()).put(eventPos, npcEventConfig.getId());
                            }
                            // 加红点
                            addRedDot(userData, eTongBingRedDotType.Type1_NpcEvent);
                            // 日志
//                            CrossAutoLogMgr.add(new LogChuHanNpcEvent(
//                                    config.getActivityInfo().getActivityId(),
//                                    userData.getUserId(),
//                                    1,
//                                    npcEventConfig.getId(),
//                                    -1,
//                                    "-1",
//                                    groupId
//                            ));
                        }
                    }
                }
            }
        }
        if (serverSyncMap.size() > 0) {
            syncServerNpcEvent(serverSyncMap);
        }
        getCrossChuHanData().setNpcEventAddTime(npcEventAddTime);
    }

    //随机npc事件位置
    private int randomNpcEventPosition(Map<Integer, Integer> userEventMap) {
        List<Integer> eventPosList = new ArrayList<>();
        int npcEventPositionNum = config.getNpcEventPositionNum();
        if (userEventMap.size() >= npcEventPositionNum) {
            return -1;
        }
        //十个位置就是0到9
        for (int i = 0; i < npcEventPositionNum; i++) {
            if (!userEventMap.containsKey(i)) {
                eventPosList.add(i);
            }
        }
        int randomValue = randomHelper.next(0, eventPosList.size());
        return eventPosList.get(randomValue);
    }

    private ChuHanNpcEventConfig randomNpcEvent() {
        Map<Integer, ChuHanNpcEventConfig> eventConfigMap = config.getNpcEventConfigMap();
        ChuHanNpcEventConfig randomNpcEventConfig = null;
        int totalWeight = 0;
        for (ChuHanNpcEventConfig npcEventConfig : eventConfigMap.values()) {
            int weight = npcEventConfig.getWeight();
            totalWeight += weight;
        }
        int randomNum = randomHelper.next(0, totalWeight);
        for (ChuHanNpcEventConfig npcEventConfig : eventConfigMap.values()) {
            int value = npcEventConfig.getWeight();
            if (randomNum < value) {
                randomNpcEventConfig = npcEventConfig;
                break;
            }
            randomNum -= value;
        }
        return randomNpcEventConfig;
    }

    //处理事件
    public void dealEvent(long userId, ChuHanProto.CrossChuHanDealEventReqMsg reqMsg) {
        ChuHanProto.ChuHanDealEventRespMsg.Builder respMsg = ChuHanProto.ChuHanDealEventRespMsg.newBuilder();
        respMsg.setRet(0);
        CrossChuHanUserData userData = getUserData(userId);
        if (!isAble(userId)) {
            respMsg.setRet(GameErrorCode.E_CHUHAN_NO_ABLE);
            sendPacket(Protocol.S_CHUHAN_DEAL_EVENT % 20000, respMsg, userId);
            return;
        }
        synchronized (lock) {
            Map<Integer, Integer> eventMap = userData.getEventMap();
            if (!eventMap.containsKey(reqMsg.getPositionNo())) {
                respMsg.setRet(GameErrorCode.E_CHUHAN_EVENT_POSITION);
                sendPacket(Protocol.S_CHUHAN_DEAL_EVENT % 20000, respMsg, userId);
                return;
            }
            int eventId = eventMap.get(reqMsg.getPositionNo());
            Map<Integer, ChuHanNpcEventConfig> eventConfigMap = config.getNpcEventConfigMap();
            ChuHanNpcEventConfig eventConfig = eventConfigMap.get(eventId);
            List<String> rewardList = eventConfig.getRewardList();
            String reward = "";
            int randomValue = -1;
            if (eventConfig.getType() == eTongBingNpcEventType.Option.getValue()) {
                if (reqMsg.getChoice() > rewardList.size() || reqMsg.getChoice() < 1) {
                    respMsg.setRet(GameErrorCode.E_CHUHAN_EVENT_CHOICE);
                    sendPacket(Protocol.S_CHUHAN_DEAL_EVENT % 20000, respMsg, userId);
                    return;
                }
                reward = rewardList.get(reqMsg.getChoice() - 1);
            } else if (eventConfig.getType() == eTongBingNpcEventType.Dispatch.getValue()) {
                randomValue = randomHelper.next(0, rewardList.size());
                reward = rewardList.get(randomValue);
            } else if (eventConfig.getType() == eTongBingNpcEventType.Event.getValue()) {
                randomValue = randomHelper.next(0, rewardList.size());
                reward = rewardList.get(randomValue);
            }
            respMsg.setRandom(randomValue);
            respMsg.setReward(reward);
            eventMap.remove(reqMsg.getPositionNo());
            userData.setUpdateOption();
            sendPacket(Protocol.S_CHUHAN_DEAL_EVENT % 20000, respMsg, userId);

            // 发游戏服加奖励
            ChuHanProto.CrossChuHanAddRewardReqMsg.Builder builder = ChuHanProto.CrossChuHanAddRewardReqMsg.newBuilder();
            builder.setReward(reward);
            builder.setLogType1(eLogMoneyType.ChuHan.getValue());
            builder.setLogType2(eLogMoneyType.ChuHanNpcEventReward.getValue());
            sendPacket(GameProtocol.S_CHUHAN_ADD_REWARD, builder, userId);

            // 日志
//            CrossAutoLogMgr.add(new LogChuHanNpcEvent(
//                    config.getActivityInfo().getActivityId(),
//                    userId,
//                    2,
//                    eventId,
//                    eventConfig.getType() == eTongBingNpcEventType.Option.getValue() ? reqMsg.getChoice() : -1,
//                    reward,
//                    groupId
//            ));
        }
    }

    public void getBattleFieldByCmd(long userId, ChuHanProto.ChuHanGetBattlefieldReqMsg reqMsg) {
        int ret = getBattleField(userId, reqMsg);
        if (ret != 0) {
            ChuHanProto.ChuHanGetBattlefieldRespMsg.Builder builder = ChuHanProto.ChuHanGetBattlefieldRespMsg.newBuilder();
            builder.setRet(ret);
            sendPacket(Protocol.S_CHUHAN_GET_BATTLE_FIELD % 20000, builder, userId);
        }
    }

    public void getCityDetailByCmd(long userId, ChuHanProto.ChuHanGetCityDetailReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();
        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);

        if (city == null) {
            ChuHanProto.ChuHanGetCityDetailRespMsg.Builder builder = ChuHanProto.ChuHanGetCityDetailRespMsg.newBuilder();
            builder.setRet(GameErrorCode.E_CHUHAN_PARAM_ERR);
            sendPacket(Protocol.S_CHUHAN_GET_CITY_DETAIL % 20000, builder, userId);
            return;
        }

        ChuHanProto.ChuHanGetCityDetailRespMsg.Builder builder = ChuHanProto.ChuHanGetCityDetailRespMsg.newBuilder();
        builder.setRet(0);
        builder.setCity(parseChuHanCityDetailMsg(city));
        sendPacket(Protocol.S_CHUHAN_GET_CITY_DETAIL % 20000, builder, userId);
    }

    public void getCityTeamByCmd(long userId, ChuHanProto.ChuHanSyncCityTeamReqMsg reqMsg) {
        getCityTeam(userId, reqMsg);
    }

    public void useWarDrumByCmd(long userId, ChuHanProto.CrossChuHanUseWarDrumReqMsg reqMsg) {
        int ret = useWarDrum(userId, reqMsg.getReq());

        ChuHanProto.ChuHanUseWarDrumRespMsg.Builder resp = ChuHanProto.ChuHanUseWarDrumRespMsg.newBuilder();
        resp.setRet(ret);

        ChuHanProto.CrossChuHanUseWarDrumRespMsg.Builder builder = ChuHanProto.CrossChuHanUseWarDrumRespMsg.newBuilder();
        builder.setResp(resp);
        builder.setCost(reqMsg.getCost());
        sendPacket(GameProtocol.S_CHUHAN_USE_WAR_DRUM_FROM_CROSS, builder, userId);
    }

    public void needHelpByCmd(long userId, ChuHanProto.ChuHanNeedHelpReqMsg reqMsg) {
        int ret = needHelp(userId, reqMsg);

        ChuHanProto.ChuHanNeedHelpRespMsg.Builder builder = ChuHanProto.ChuHanNeedHelpRespMsg.newBuilder();
        builder.setRet(ret);
        if (ret == 0) {
            builder.setSyncNeedHelpTime(getUserData(userId).getNeedHelpTime());
        }
        sendPacket(Protocol.S_CHUHAN_NEED_HELP % 20000, builder, userId);
    }

    public void getBattlefieldRankByCmd(long userId, ChuHanProto.ChuHanGetBattlefieldRankReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();
        CrossChuHanCityData city = getCrossChuHanData().getCityMap().get(cityId);

        ChuHanProto.ChuHanGetBattlefieldRankRespMsg.Builder builder = ChuHanProto.ChuHanGetBattlefieldRankRespMsg.newBuilder();
        if (city == null) {
            builder.setRet(GameErrorCode.E_CHUHAN_CITY_NO_EXIST);
            sendPacket(Protocol.S_CHUHAN_GET_BATTLEFIELD_RANK % 20000, builder, userId);
            return;
        }
        CrossChuHanBattlefield battlefield = city.getBattlefield();
        if (battlefield == null) {
            builder.setRet(GameErrorCode.E_CHUHAN_NO_BATTLE);
            sendPacket(Protocol.S_CHUHAN_GET_BATTLEFIELD_RANK % 20000, builder, userId);
            return;
        }

        builder.setRet(0);

        int rankNum = 0;
        int myRank = -1;
        long myValue = 0;
        for (CrossChuHanBattlefieldRank rank : new LinkedList<>(battlefield.getBattlefieldRankNewList())) {
            rankNum++;
            if (rank.getUserId() == userId) {
                myValue = rank.getZhanGong();
                myRank = rankNum;
            }
            builder.addList(parseChuHanBattlefieldRankMsg(rank));
        }

        builder.setMyRank(myRank);
        builder.setMyValue(myValue);
        sendPacket(Protocol.S_CHUHAN_GET_BATTLEFIELD_RANK % 20000, builder, userId);
    }

    public void sendChatByCmd(long userId, CrossActivityProto.CrossActivityChatReqMsg.Builder reqMsg) {
        int range = 0;
        int id = 0;
        CrossChuHanUserData userData = getUserData(userId);
        if (eMessageType.ChuHanPublicChat.getValue() == reqMsg.getMsg().getType()) {
            range = CrossChuHanChatLogic.RANGE_ALL;
            id = config.getActivityInfo().getActivityId();
            // 公屏聊天加上阵营
            WorldMessageProto.WorldMessageTempMsg.Builder message = reqMsg.getMsg().toBuilder();
            message.setExtraParams(String.valueOf(userData == null ? 0 : userData.getCampId()));
            reqMsg.setMsg(message);
        } else if (eMessageType.ChuHanCampChat.getValue() == reqMsg.getMsg().getType()) {
            range = CrossChuHanChatLogic.RANGE_CAMP;
            id = userData == null ? 0 : userData.getCampId();
        } else if (eMessageType.ChuHanBattlefieldChat.getValue() == reqMsg.getMsg().getType()) {
            range = CrossChuHanChatLogic.RANGE_BATTLEFIELD;
            UserScene userScene = CrossSceneMgr.getUserScene(userId);
            if (userScene != null && eSceneType.TongBingBattlefield.getValue() == userScene.getSceneType()) {
                try {
                    String cityIdStr = userScene.getSceneId().split("-")[1];
                    id = Integer.valueOf(cityIdStr);
                } catch (Exception e) {
                }
            }
        }

        if (range <= 0 || id <= 0) {
            return;
        }

        chatLogic.chat(range, id, 1, reqMsg.getMsg());
    }

    public void getChatByCmd(long userId, List<Integer> paramList, int param2) {
        List<WorldMessageProto.WorldMessageTempMsg> dataList = new ArrayList<>();
        if (paramList.isEmpty()) {
            return;
        }
        CrossChuHanUserData userData = getUserData(userId);
        for (int range : paramList) {
            int id = 0;
            if (range == CrossChuHanChatLogic.RANGE_ALL) {
                id = config.getActivityInfo().getActivityId();
            } else if (range == CrossChuHanChatLogic.RANGE_CAMP) {
                id = userData == null ? 0 : userData.getCampId();
            } else if (range == CrossChuHanChatLogic.RANGE_BATTLEFIELD) {
                // 城池id
                id = param2;
            }

            if (range <= 0 || id <= 0) {
                continue;
            }
            dataList.addAll(chatLogic.getChatMsg(range, id));
        }

        CrossActivityProto.CrossActivityChatListRespMsg.Builder respMsg = CrossActivityProto.CrossActivityChatListRespMsg.newBuilder();
        respMsg.setActivityId(config.getActivityInfo().getActivityId());
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        if (userBaseInfo != null) {

            if (dataList.size() > 0) {
                dataList.sort(Comparator.comparingLong(WorldMessageProto.WorldMessageTempMsg::getSendTime));
                WorldMessageProto.WorldMessageListMsg.Builder worldMessageListMsg = WorldMessageProto.WorldMessageListMsg.newBuilder();
                worldMessageListMsg.addAllDataList(dataList);
                respMsg.setDataList(worldMessageListMsg);
            }
            YanQuMessage pbMsg = YanQuMessageUtils.buildMessage(Protocol.U_CROSS_ACTIVITY_CHAT_LIST, respMsg);
            MessageHelper.sendPacket(userBaseInfo.getServerId(), userId, pbMsg);
        }
    }

    public void getRankRewardByCmd(long userId, ChuHanProto.ChuHanGetRankRewardReqMsg reqMsg) {
        int ret = getRankReward(userId, reqMsg.getConditionType());
        if (ret != 0) {
            ChuHanProto.ChuHanGetRankRewardRespMsg.Builder builder = ChuHanProto.ChuHanGetRankRewardRespMsg.newBuilder();
            builder.setRet(ret);
            sendPacket(Protocol.S_CHUHAN_GET_RANK_REWARD % 20000, builder, userId);
        }
    }

    public void getCampRankListByCmd(long userId) {
        ChuHanProto.ChuHanGetCampRankRespMsg.Builder builder = ChuHanProto.ChuHanGetCampRankRespMsg.newBuilder();
        builder.setRet(0);

        for (CrossChuHanRank rank : getCampScoreRankList()) {
            builder.addRank(parseChuHanCampRankMsg(rank));
        }

        sendPacket(Protocol.S_CHUHAN_GET_CAMP_RANK_LIST % 20000, builder, userId);
    }

    public void getCampUnionRankListByCmd(long userId, ChuHanProto.ChuHanGetCampUnionRankReqMsg reqMsg) {
        int campId = reqMsg.getCampId();
        CrossChuHanUserData userData = getUserData(userId);
        String unionUid = "";
        if (userData != null) {
            unionUid = userData.getUnionUid();
        }

        ChuHanProto.ChuHanGetCampUnionRankRespMsg.Builder builder = ChuHanProto.ChuHanGetCampUnionRankRespMsg.newBuilder();
        builder.setRet(0);
        List<CrossChuHanRank> rankList = new ArrayList<>();
        if (campId == 0) {
            // 获取全部商会
            for (CrossChuHanRankListModel model : campUnionRankMap.values()) {
                if (model != null) {
                    rankList.addAll(model.getRankList());
                }
            }
        } else {
            CrossChuHanRankListModel model = campUnionRankMap.get(campId);
            if (model != null) {
                rankList.addAll(model.getRankList());
            }
        }
        rankList.sort(new CrossChuHanRankSort());

        int rankShowLimit = unionMaxRank;
        if (campId == 0) {
            rankShowLimit = unionMaxRank * config.getCampNum();
        }
        // 获取商会排名
        int num = 0;
        for (CrossChuHanRank rank : rankList) {
            num++;
            if (num > rankShowLimit) {
                break;
            }
            builder.addRank(parseChuHanCampUnionRankMsg(rank));
        }

        // 获取商会阵营排名
        int myRank = -1;
        int myCampId = userData == null ? 0 : userData.getCampId();
        if (myCampId != 0) {
            CrossChuHanRankListModel model = campUnionRankMap.get(myCampId);
            if (model != null) {
                int cnt = 0;
                for (CrossChuHanRank rank : model.getRankList()) {
                    cnt++;
                    if (cnt > rankShowLimit) {
                        break;
                    }
                    if (Objects.equals(rank.getId(), unionUid)) {
                        myRank = cnt;
                    }
                }
            }
        }
        builder.setMyRank(myRank);

        long myValue = 0L;
        CrossChuHanUnionJoin unionJoin = getUnionJoin(unionUid);
        if (unionJoin != null) {
            for (CrossChuHanUnionJoinMember member : unionJoin.getMemberMap().values()) {
                CrossChuHanUserData tmp = getUserData(member.getUserId());
                myValue += tmp.getZhanGong();
            }
        }
        builder.setMyValue(myValue);

        sendPacket(Protocol.S_CHUHAN_GET_CAMP_UNION_RANK_LIST % 20000, builder, userId);
    }

    public void getUserRankListByCmd(long userId, ChuHanProto.ChuHanGetUserRankReqMsg reqMsg) {
        int type = reqMsg.getType();
        CrossChuHanUserData userData = getUserData(userId);
        long myValue = 0;
        if (userData != null) {
            myValue = getUserData(userId).getZhanGong();
        }
        String myId = String.valueOf(userId);
        LinkedList<CrossChuHanRank> rankList = new LinkedList<>();
        int maxRank = 100;

        // 【查其他玩家排名】
        if (type == 3) {
            getOtherUserRankByCmd(userId, reqMsg.getParam());
            return; // ！！！
        }

        if (type == 1) {
            // 【个人榜】
            rankList = userRank.getRankList();
            maxRank = userMaxRank;
        } else if (type == 2) {
            // 【商会内个人榜】
            String unionUid = reqMsg.getParam();
            CrossChuHanUnionJoin unionJoin = getUnionJoin(unionUid);
            if (StringUtils.isNullOrEmpty(unionUid) || unionJoin == null) {
                return; // 参数错误
            }
            // 组装个榜单
            CrossChuHanRankListModel tmpModel = new CrossChuHanRankListModel();
            LinkedList<CrossChuHanRank> tmpList = new LinkedList<>();
            for (Long memberId : unionJoin.getMemberMap().keySet()) {
                CrossChuHanUserData member = getUserData(memberId);
                tmpList.add(new CrossChuHanRank(String.valueOf(member.getUserId()), member.getZhanGong(), 0L));
            }
            tmpModel.setRankList(tmpList); // 排序
            rankList = tmpModel.getRankList();
        } else if (type == 4) {//pve个人榜
            int cityType = Integer.valueOf(reqMsg.getParam());
            CrossChuHanRankListModel pveRankModel = pveUserRankMap.get(cityType);
            if (pveRankModel != null) {
                rankList = pveRankModel.getRankList();
            }
            maxRank = getConfig().getPveMaxRank();
            myValue = userData.getPveZhanGong(cityType);
        } else {
            return; // 参数错误
        }

        ChuHanProto.ChuHanGetUserRankRespMsg.Builder builder = ChuHanProto.ChuHanGetUserRankRespMsg.newBuilder();
        builder.setRet(0);

        int myRank = -1;
        int rankNum = 0;
        for (CrossChuHanRank rank : rankList) {
            rankNum++;
            if (rankNum > maxRank) {
                break;
            }
            if (Objects.equals(rank.getId(), myId)) {
                myRank = rankNum;
            }
            builder.addRank(parseChuHanUserRankMsg(rank));
        }

        builder.setMyRank(myRank);
        builder.setMyValue(myValue);
        sendPacket(Protocol.S_CHUHAN_GET_USER_RANK_LIST % 20000, builder, userId);
    }

    public void getOtherUserRankByCmd(long userId, String otherUserId) {
        int otherRank = -1;
        long otherValue = 0L;

        int num = 0;
        for (CrossChuHanRank rank : userRank.getRankList()) {
            num++;
            if (num > userMaxRank) {
                break;
            }
            if (Objects.equals(rank.getId(), otherUserId)) {
                otherRank = num;
                otherValue = rank.getValue();
                break;
            }
        }

        ChuHanProto.ChuHanGetUserRankRespMsg.Builder builder = ChuHanProto.ChuHanGetUserRankRespMsg.newBuilder();
        builder.setRet(0);
        builder.setMyRank(otherRank);
        builder.setMyValue(otherValue);
        // builder.setRank(); // 传排名，不需要传rankList
        sendPacket(Protocol.S_CHUHAN_GET_USER_RANK_LIST % 20000, builder, userId);
    }

    public void readRedDotByCmd(long userId, ChuHanProto.ChuHanReadRedDotReqMsg reqMsg) {
        CrossChuHanUserData userData = getUserData(userId);
        if (isAble(userId)) {
            userData.getRedDotMap().remove(reqMsg.getType());
            userData.setUpdateOption();
        }
    }

    // ============================== SYNC ==============================

    public void syncBaseDataMsg(CrossChuHanUserData userData) {
        if (userData == null) {
            return;
        }

        int joinStatus = 0; // 0未报名、1已报名、2有资格、3商会未入选
        if (getCrossChuHanData().isLockUnion()) {
            joinStatus = 2;
        } else {
            for (CrossChuHanUnionJoin join : unionJoinMap.values()) {
                if (join.getMemberMap().containsKey(userData.getUserId())) {
                    joinStatus = 1;
                    break;
                }
            }
        }

        ChuHanProto.ChuHanSyncBaseDataRespMsg.Builder builder = ChuHanProto.ChuHanSyncBaseDataRespMsg.newBuilder();
        builder.setJoinStatus(joinStatus);
        if (userData.getCampId() != 0) {
            builder.setCampId(userData.getCampId());
        }
        builder.setSyncPatronsTime(userData.getLastSyncPatronsTime());
        builder.setGuide(userData.getGuide());
        builder.setSyncNeedHelpTime(userData.getNeedHelpTime());
        builder.setFlitTime(userData.getFlitTime());
        builder.setHomeCityId(userData.getBirthCityId());

        sendPacket(Protocol.S_CHUHAN_SYNC_BASE_DATA % 20000, builder, userData.getUserId());
    }

    /**
     * 跨服同步用户数据到游戏服
     */
    public void syncUserDataToGameServer(CrossChuHanUserData userData) {
        if (userData == null) {
            return;
        }
        ChuHanProto.CrossChuHanSyncUserDataMsg.Builder builder = ChuHanProto.CrossChuHanSyncUserDataMsg.newBuilder();
        builder.setCampId(userData.getCampId());
        builder.setZhanGong(userData.getZhanGong());
        sendPacket(GameProtocol.S_CROSS_CHUHAN_SYNC_USER_DATA, builder, userData.getUserId());
    }

    /**
     * 请求游戏服上传玩家信息
     */
    public void sendGetUnionMemberMsg() {
        // 检查报名的商会中，区服未上传数据的成员
        Map<Long, List<Long>> serverUserMap = new ConcurrentHashMap<>();
        for (CrossChuHanUnionJoin unionJoin : unionJoinMap.values()) {
            for (CrossChuHanUnionJoinMember joinMember : unionJoin.getMemberMap().values()) {
                serverUserMap.putIfAbsent(joinMember.getServerId(), new ArrayList<>());
                serverUserMap.get(joinMember.getServerId()).add(joinMember.getUserId());
            }
        }

        for (Map.Entry<Long, List<Long>> entry : serverUserMap.entrySet()) {
            long serverId = entry.getKey();
            List<Long> userId = entry.getValue();
            if (userId.size() > 0) {
                ChuHanProto.CrossChuHanGetMemberReqMsg.Builder builder = ChuHanProto.CrossChuHanGetMemberReqMsg.newBuilder();
                builder.setActivityId(config.getActivityInfo().getActivityId());
                builder.addAllUserId(userId);
                builder.setGroupId(groupId);
                MessageHelper.sendPacket(serverId, 0, YanQuMessageUtils.buildMessage(GameProtocol.S_CROSS_CHUHAN_GET_UNION_MEMBER_REQ, builder));

            }
        }
    }

    public void syncTeamEnterBattlefield(CrossChuHanCityData city, CrossChuHanBattlefield battlefield, CrossChuHanBattlefieldPart part) {

        // 获取场景内玩家
        List<Long> userIds = getBattlefieldUserIdList(city.getCityId(), part.getPartId());

        ChuHanProto.ChuHanSyncTeamEnterRespMsg.Builder builder = ChuHanProto.ChuHanSyncTeamEnterRespMsg.newBuilder();
        builder.setBattlefield(parseChuHanBattlefieldMsg(part, battlefield, city));

        sendPacketToAll(Protocol.S_CHUHAN_SYNC_TEAM_ENTER % 20000, builder, userIds);
    }

    public void syncUseWarDrum(CrossChuHanCityData city, CrossChuHanBattlefield battlefield, CrossChuHanBattlefieldPart part) {

        // 获取场景内玩家
        List<Long> userIds = getBattlefieldUserIdList(city.getCityId(), part.getPartId());

        ChuHanProto.ChuHanSyncUseDrumRespMsg.Builder builder = ChuHanProto.ChuHanSyncUseDrumRespMsg.newBuilder();
        builder.setBattlefield(parseChuHanBattlefieldMsg(part, battlefield, city));

        sendPacketToAll(Protocol.S_CHUHAN_SYNC_WAR_DRUM % 20000, builder, userIds);
    }

    public void syncBattleResultMsg(List<CrossChuHanBattleResult> resultList, CrossChuHanCityWarReport report) {
        for (CrossChuHanBattleResult result : resultList) {

            // 获取战场内玩家
            List<Long> userIds = getBattlefieldUserIdList(result.getCity().getCityId(), result.getPart().getPartId());
            if (userIds.isEmpty()) {
                continue; // 没人在里面，不同步了
            }

            // 战场数据
            ChuHanProto.ChuHanBattlefieldMsg.Builder battlefieldMsg = parseChuHanBattlefieldMsg(result.getPart(), result.getBattlefield(), result.getCity());

            // 战斗结果数据
            ChuHanProto.ChuHanBattleResultMsg.Builder battleResultMsg = ChuHanProto.ChuHanBattleResultMsg.newBuilder();
            battleResultMsg.setDefWin(result.getWinType() == 1);
            battleResultMsg.setDefHpReduce(result.getDefHpReduce());
            battleResultMsg.setAtkHpReduce(result.getAtkHpReduce());
            battleResultMsg.setBattlefield(battlefieldMsg);
            if (report != null) {
                battleResultMsg.setResult(parseChuHanCityWarReportMsg(report));
            }
            battleResultMsg.setWinTimes(result.getWinTimes());

            // 返回值
            ChuHanProto.ChuHanSyncBattleResultMsg.Builder builder = ChuHanProto.ChuHanSyncBattleResultMsg.newBuilder();
            builder.setBattle(battleResultMsg);

            sendPacketToAll(Protocol.S_CHUHAN_BATTLE_RESULT % 20000, builder, userIds);
        }
    }

    public void syncBarracksMsg(long userId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (isInActivity(userData, System.currentTimeMillis())) {
            ChuHanProto.ChuHanSyncBarracksRespMsg.Builder respMsg = ChuHanProto.ChuHanSyncBarracksRespMsg.newBuilder();
            respMsg.setBarracks(parseChuHanBarracksMsg(userId));
            sendPacket(Protocol.S_CHUHAN_SYNC_BARRACKS % 20000, respMsg, userId);
        }
    }

    public void syncCityTeamToSelf(CrossChuHanCityData city) {
        // 拿所有队伍
        List<CrossChuHanTeam> teamList = new ArrayList<>();
        for (CrossChuHanTeamArrive teamArrive : new LinkedList<>(city.getAtkTeamList())) {
            teamList.add(getTeamByArrive(teamArrive));
        }
        for (CrossChuHanTeamArrive teamArrive : new LinkedList<>(city.getDefTeamList())) {
            teamList.add(getTeamByArrive(teamArrive));
        }
        CrossChuHanBattlefield battlefield = city.getBattlefield();
        if (battlefield != null) {
            for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                for (CrossChuHanBattlefieldTeam tmp : new LinkedList<>(part.getAtkList())) {
                    teamList.add(getTeamByArrive(tmp.getTeam()));
                }
                for (CrossChuHanBattlefieldTeam tmp : new LinkedList<>(part.getDefList())) {
                    if (tmp.getTeam() != null) {
                        teamList.add(getTeamByArrive(tmp.getTeam()));
                    }
                }
            }
        }

        // 按玩家分类
        Map<Long, List<CrossChuHanTeam>> userTeamMap = new ConcurrentHashMap<>();
        for (CrossChuHanTeam team : teamList) {
            userTeamMap.putIfAbsent(team.getUserId(), new ArrayList<>());
            userTeamMap.get(team.getUserId()).add(team);
        }

        // 按玩家同步
        for (Map.Entry<Long, List<CrossChuHanTeam>> entry : userTeamMap.entrySet()) {
            syncTeamToSelf(entry.getKey(), entry.getValue());
        }
    }

    public void syncTeamToSelf(long userId, List<CrossChuHanTeam> teamList) {
        long now = System.currentTimeMillis();
        if (!isInActivity(getUserData(userId), now)) {
            return;
        }

        ChuHanProto.ChuHanSyncTeamRespMsg.Builder builder = ChuHanProto.ChuHanSyncTeamRespMsg.newBuilder();
        for (CrossChuHanTeam team : teamList) {
            builder.addTeam(parseChuHanTeamMsg(getUserData(team.getUserId()), team));
        }
        sendPacket(Protocol.S_CHUHAN_SYNC_TEAM % 20000, builder, userId);
    }

    /**
     * 同步发光数据
     */
    private void syncLight() {
        List<Long> userIdList = new ArrayList<>();
        for (CrossChuHanRank rank : userRank.getRankList()) {
            if (userIdList.size() >= config.getLightRankNum() || rank.getValue() < config.getLightNeedZhanGong()) {
                break;
            }
            userIdList.add(Long.valueOf(rank.getId()));
        }

        // 获取在活动内的玩家
        long now = System.currentTimeMillis();
        List<Long> recUsers = userDataMap.values().stream().filter(item -> isInActivity(item, now)).map(CrossChuHanUserData::getUserId).collect(Collectors.toList());
        if (recUsers.isEmpty()) {
            return;
        }

        ChuHanProto.ChuHanSyncLightUserRespMsg.Builder builder = ChuHanProto.ChuHanSyncLightUserRespMsg.newBuilder();
        builder.addAllUserId(userIdList);
        sendPacketToAll(Protocol.S_CHUHAN_SYNC_LIGHT_USER % 20000, builder, recUsers);
    }

    public void syncBuffToAll() {
        if (isNeedSyncBuff.compareAndSet(true, false)) {
            // 获取在活动内的玩家
            long now = System.currentTimeMillis();
            Map<Integer,List<Long>> needSyncUserMap = new HashMap<>();
            for (CrossChuHanUserData userData : userDataMap.values()) {
                if (!isInActivity(userData, now)){
                    continue;
                }
                int campId = userData.getCampId();
                if (!needSyncUserMap.containsKey(campId)) {
                    needSyncUserMap.put(campId, new ArrayList<>());
                }
                needSyncUserMap.get(campId).add(userData.getUserId());
            }

            for (Map.Entry<Integer, List<Long>> entry : needSyncUserMap.entrySet()) {
                int campId = entry.getKey();
                List<Long> userIdList = entry.getValue();

                ChuHanProto.ChuHanSyncBuffRespMsg.Builder builder = ChuHanProto.ChuHanSyncBuffRespMsg.newBuilder();
                List<List<Integer>> effectCampBuffList = getEffectCampBuffList(campId);
                for (List<Integer> buffInfos : effectCampBuffList) {
                    int buffId = buffInfos.get(0);
                    int buffLevel = buffInfos.get(1);
                    builder.addBuff(parseChuHanBuffMsg(buffId, buffLevel));
                }
                sendPacketToAll(Protocol.S_CHUHAN_SYNC_BUFF % 20000, builder, userIdList);
            }
        }
    }

    /**
     * 同步指挥官信息给阵营所有玩家
     *
     * @param campId
     */
    private void syncCommanderInfoToCamp(int campId) {
        if (campId == 0) {
            return;
        }
        ChuHanProto.ChuHanCommanderDataSyncMsg.Builder syncMsg = parseCommanderInfoMsg(campId);
        // 同步给阵营所有玩家
        List<Long> campUserList = userDataMap.values().stream().filter(item -> item.getCampId() == campId).mapToLong(CrossChuHanUserData::getUserId).boxed().collect(Collectors.toList());
        sendPacketToAll(Protocol.S_CHUHAN_COMMANDER_DATA_SYNC % 20000, syncMsg, campUserList);
    }

    /**
     * 同步指挥官信息
     */
    private void syncCommanderInfo(long userId, int campId) {
        if (campId == 0) {
            return;
        }
        ChuHanProto.ChuHanCommanderDataSyncMsg.Builder syncMsg = parseCommanderInfoMsg(campId);
        sendPacket(Protocol.S_CHUHAN_COMMANDER_DATA_SYNC % 20000, syncMsg, userId);
    }

    private ChuHanProto.ChuHanCommanderDataSyncMsg.Builder parseCommanderInfoMsg(int campId) {
        ChuHanProto.ChuHanCommanderDataSyncMsg.Builder syncMsg = ChuHanProto.ChuHanCommanderDataSyncMsg.newBuilder();
        ChuHanProto.ChuHanCommanderDataTemp.Builder commanderDataMsg = ChuHanProto.ChuHanCommanderDataTemp.newBuilder();
        CrossChuHanCampData crossChuHanCampData = campDataMap.get(campId);
        CrossChuHanCommanderData commanderData = crossChuHanCampData.getCommanderData();
        if (commanderData != null) {
            commanderDataMsg.setUserId(commanderData.getUserId());
            UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(commanderData.getUserId());
            if (userBaseInfo != null) {
                commanderDataMsg.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
            }
            commanderDataMsg.setNotice(commanderData.getNotice());
            commanderDataMsg.setMajorCityId(commanderData.getMajorCityId());
            commanderDataMsg.setMinorCityId(commanderData.getMinorCityId());
            syncMsg.setCommanderData(commanderDataMsg);
        }
        return syncMsg;
    }

    // ============================== PB ==============================

    public ChuHanProto.ChuHanBuffMsg.Builder parseChuHanBuffMsg(int buffId, int level) {
        ChuHanProto.ChuHanBuffMsg.Builder builder = ChuHanProto.ChuHanBuffMsg.newBuilder();
        builder.setBuffId(buffId);
        builder.setLevel(level);
        return builder;
    }

    public ChuHanProto.ChuHanCityEventMsg.Builder parseChuHanCityEventMsg(CrossChuHanCityEvent cityEvent) {
        ChuHanProto.ChuHanCityEventMsg.Builder builder = ChuHanProto.ChuHanCityEventMsg.newBuilder();
        builder.setCityId(cityEvent.getCityId());
        builder.setBuffId(cityEvent.getBuffId());
        builder.setRefreshTime(cityEvent.getRefreshTime());
        if (cityEvent.getEffectCamp() != 0) {
            builder.setEffectCampId(cityEvent.getEffectCamp());
            builder.setEndTime(cityEvent.getEndTime());
        }
        return builder;
    }

    public ChuHanProto.ChuHanTeamMsg.Builder parseChuHanTeamMsg(CrossChuHanUserData userData, CrossChuHanTeam team) {
        // 获取主将
        int patrons1 = 0;
        int skin1 = 0;
        if (!team.getPatronsList().isEmpty()) {
            patrons1 = team.getPatronsList().get(0);
            CrossChuHanPatrons patrons = userData.getPatronsMap().get(patrons1);
            if (patrons != null) {
                skin1 = patrons.getSkin();
            }
        }

        ChuHanProto.ChuHanTeamMsg.Builder builder = ChuHanProto.ChuHanTeamMsg.newBuilder();
        builder.setOccupation(team.getOccupation());
        builder.setPatronsId(patrons1);
        builder.setTeamStatus(getTeamStatus(team).getValue());
        builder.setCityId(team.getCityId());
        builder.addAllToCityId(new ArrayList<>(team.getRouteList()));
        builder.setStartTime(team.getBeginMoveTime());
        builder.setNextCityArriveTime(team.getArriveTime());
        builder.setLastCityDepartureTime(team.getSmallMoveTime());
        builder.setMaxSoldier(team.getMaxSoldier());
        builder.setSoldierNum(team.getSoldierNum());
        builder.setUser(parseChuHanUserMsg(userData.getUserId()));
        builder.setSkinId(skin1);
        builder.setHorseId(userData.getHorseId());
        builder.setTargetCityId(team.getTargetCityId());
        return builder;
    }

    public ChuHanProto.ChuHanCityMsg.Builder parseChuHanCityMsg(CrossChuHanCityData city) {
        // 准备几个参数
        Set<Integer> atkCampId = new HashSet<>();
        int atkNum = 0;
        int defNum = 0;
        long helpNum = 0;

        CrossChuHanBattlefield battlefield = city.getBattlefield();
        if (battlefield != null) {
            helpNum = battlefield.getHelpNum();
            for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                // 进攻、防守方数量
                atkNum += part.getAtkList().size();
                defNum += part.getDefList().size();
                if (atkCampId.size() < config.getCampNum() - 1) {
                    // 遍历攻击方找攻击方阵营
                    for (CrossChuHanBattlefieldTeam atkTempTeam : new LinkedList<>(part.getAtkList())) {
                        CrossChuHanTeamArrive arrive = atkTempTeam.getTeam();
                        if (arrive != null) {
                            atkCampId.add(getUserData(arrive.getUserId()).getCampId());
                        }
                    }
                }
            }
        }

        atkNum += city.getAtkTeamList().size();
        if (atkCampId.size() < config.getCampNum() - 1) {
            for (CrossChuHanTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
                atkCampId.add(getUserData(arrive.getUserId()).getCampId());
            }
        }

        defNum = defNum + city.getDefTeamList().size() + getSoldierTeamCount(city);

        ChuHanProto.ChuHanCityMsg.Builder builder = ChuHanProto.ChuHanCityMsg.newBuilder();
        builder.setCityId(city.getCityId());
        builder.setCampId(city.getCampId());
        builder.setDefTeamNum(defNum);
        builder.setAtkTeamNum(atkNum);
        builder.setNeedHelpNum(helpNum);
        builder.addAllAtkCampId(new ArrayList<>(atkCampId));
        builder.setTeamRecTime(city.getTeamRecoveryBeginTime() == 0 ? 0 : city.getTeamRecoveryBeginTime() + config.getCityTeamRecoveryTime());
        return builder;
    }

    public ChuHanProto.ChuHanCityDetailMsg.Builder parseChuHanCityDetailMsg(CrossChuHanCityData city) {

        ChuHanCityConfig cityConfig = config.getCityConfigMap().get(city.getCityId());
        ChuHanCityTypeConfig typeConfig = config.getCityTypeConfigMap().get(cityConfig.getType());

        Map<Long, CrossChuHanDefUser> defUserMap = new HashMap<>();
        int defTeamNum = city.getTeamNum(); // 城防军数量
        int extraTeamNum = city.getExtraTeamNum(); // 额外的城防军数量
        int defUserTeamNum = 0; // 玩家队伍数量

        // 战场内拿防守队伍
        CrossChuHanBattlefield battlefield = city.getBattlefield();
        if (battlefield != null) {
            for (CrossChuHanBattlefieldPart part : battlefield.getPartMap().values()) {
                for (CrossChuHanBattlefieldTeam battlefieldTeam : new LinkedList<>(part.getDefList())) {
                    if (battlefieldTeam.getTeam() == null) {
                        if (battlefieldTeam.getSoldierType() == eTongBingSoldierType.COMMON_SOLDIER.getValue()) {
                            defTeamNum++;
                        } else if (battlefieldTeam.getSoldierType() == eTongBingSoldierType.EXTRA_SOLDIER.getValue()) {
                            extraTeamNum++;
                        }
                    } else {
                        CrossChuHanTeam team = getTeamByArrive(battlefieldTeam.getTeam());
                        if (!defUserMap.containsKey(team.getUserId())) {
                            defUserMap.put(team.getUserId(), new CrossChuHanDefUser(team.getUserId()));
                        }
                        CrossChuHanDefUser defUser = defUserMap.get(team.getUserId());
                        defUser.setTeamNum(defUser.getTeamNum() + 1);
                        defUser.setSoldierNum(defUser.getSoldierNum() + team.getSoldierNum());
                    }
                }
            }
        }
        // 排队里拿防守队伍
        for (CrossChuHanTeamArrive arrive : new LinkedList<>(city.getDefTeamList())) {
            CrossChuHanTeam team = getTeamByArrive(arrive);
            if (!defUserMap.containsKey(team.getUserId())) {
                defUserMap.put(team.getUserId(), new CrossChuHanDefUser(team.getUserId()));
            }
            CrossChuHanDefUser defUser = defUserMap.get(team.getUserId());
            defUser.setTeamNum(defUser.getTeamNum() + 1);
            defUser.setSoldierNum(defUser.getSoldierNum() + team.getSoldierNum());
        }

        // 计算防守玩家队伍数
        for (CrossChuHanDefUser defUser : defUserMap.values()) {
            defUserTeamNum += defUser.getTeamNum();
        }

        // 是否当前阵营的出生城池
        boolean isBirthCity = cityConfig.getType() == eChuHanCityType.Type1_MainCity.getValue() && cityConfig.getInitialCamp() == city.getCampId();

        // 防守队伍设置个显示上限
        List<CrossChuHanDefUser> sortDefUserList = new ArrayList<>();
        if (!isBirthCity) {
            sortDefUserList = defUserMap.values().stream().sorted(Comparator.comparing(CrossChuHanDefUser::getSoldierNum).reversed())
                    .limit(50).collect(Collectors.toList()); // 写死50个
        }

        // 返回值
        ChuHanProto.ChuHanCityDetailMsg.Builder builder = ChuHanProto.ChuHanCityDetailMsg.newBuilder();
        builder.setDefTeamNum(defTeamNum);
        builder.setExtraDefTeamNum(extraTeamNum);
        builder.setDefTeamSoldierNum(city.getTeamSoldierNum());
        builder.setFoodProduction(typeConfig.getFoodProduction());
        for (CrossChuHanDefUser tmpDefTeam : sortDefUserList) {
            UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(tmpDefTeam.getUserId());
            String nickname = userBaseInfo == null ? "" : userBaseInfo.getNickName();

            ChuHanProto.ChuHanCityDefUserMsg.Builder tmp = ChuHanProto.ChuHanCityDefUserMsg.newBuilder();
            tmp.setUserId(tmpDefTeam.getUserId());
            tmp.setName(nickname);
            tmp.setTeamNum(tmpDefTeam.getTeamNum());
            tmp.setTotalValue(tmpDefTeam.getSoldierNum());

            builder.addDefUser(tmp);
        }
        builder.setTotalTeamNum(defTeamNum + extraTeamNum + defUserTeamNum);
        return builder;
    }

    public ChuHanProto.ChuHanBarracksMsg.Builder parseChuHanBarracksMsg(long userId) {
        ChuHanProto.ChuHanBarracksMsg.Builder builder = ChuHanProto.ChuHanBarracksMsg.newBuilder();
        CrossChuHanUserData userData = getUserData(userId);
        CrossChuHanBarracks barracks = userData.getBarracks();
        builder.setReadyNum(barracks.getReadyNum());
        builder.setReadyMaxNum(barracks.getReadyMaxNum());
        builder.setCreateNum(barracks.getCreateNum());
        builder.setCreateTime(barracks.getCreateTime());
        builder.setCreateSpeed(barracks.getCreateSpeed() == 0 ? 0 : DateHelper.SECOND_MILLIONS / barracks.getCreateSpeed());
        builder.setCreateCount(barracks.getCreateCount());
        builder.setWoundedNum(barracks.getWoundedNum());
        builder.setWoundedMaxNum(barracks.getHospitalSize());
        builder.setRescueNum(barracks.getRescueNum());
        builder.setRescueTime(barracks.getRescueTime());
        builder.setRescueSpeed(barracks.getRescueSpeed());
        builder.setRescueCount(barracks.getRescueCount());

        builder.setSyncTime(DateHelper.getCurrentTime());
        return builder;
    }

    public ChuHanProto.ChuHanCityWarReportMsg.Builder parseChuHanCityWarReportMsg(CrossChuHanCityWarReport report) {
        ChuHanProto.ChuHanCityWarReportMsg.Builder builder = ChuHanProto.ChuHanCityWarReportMsg.newBuilder();
        builder.addAllAtkCampId(report.getAtkCampId());
        builder.setDefCampId(report.getDefCampId());
        builder.setType(report.getType());
        builder.setCityId(report.getCityId());
        builder.setTime(report.getTime());
        for (CrossChuHanBattlefieldRank rank : report.getAtkRankList()) {
            builder.addAtkRank(parseChuHanBattlefieldRankMsg(rank));
        }
        for (CrossChuHanBattlefieldRank rank : report.getDefRankList()) {
            builder.addDefRank(parseChuHanBattlefieldRankMsg(rank));
        }
        builder.setWinCampId(report.getWinCampId());
        return builder;
    }

    public int getDefTeamNum(CrossChuHanCityData city, CrossChuHanBattlefield battlefield, int type) {
        int num;
        if (type == eTongBingSoldierType.COMMON_SOLDIER.getValue()) {
            num = city.getTeamNum();
        } else if (type == eTongBingSoldierType.EXTRA_SOLDIER.getValue()) {
            num = city.getExtraTeamNum();
        } else {
            return 0;
        }
        for (CrossChuHanBattlefieldPart crossChuHanBattlefieldPart : battlefield.getPartMap().values()) {
            // 统计守军数量
            for (CrossChuHanBattlefieldTeam team : new LinkedList<>(crossChuHanBattlefieldPart.getDefList())) {
                if (team.getTeam() == null) {
                    if (team.getSoldierType() == type) {
                        num++;
                    }
                }
            }
        }
        return num;
    }

    public ChuHanProto.ChuHanBattlefieldMsg.Builder parseChuHanBattlefieldMsg(CrossChuHanBattlefieldPart part,
                                                                              CrossChuHanBattlefield battlefield,
                                                                              CrossChuHanCityData city) {

        int cityDefTeamNum = city.getTeamNum();
        int cityExtraDefTeamNum = city.getExtraTeamNum();
        int defNum = city.getDefTeamList().size() + city.getTeamNum() + city.getExtraTeamNum();
        int atkNum = city.getAtkTeamList().size();
        for (CrossChuHanBattlefieldPart crossChuHanBattlefieldPart : battlefield.getPartMap().values()) {
            defNum += crossChuHanBattlefieldPart.getDefList().size();
            atkNum += crossChuHanBattlefieldPart.getAtkList().size();
            // 统计守军数量
            for (CrossChuHanBattlefieldTeam team : new LinkedList<>(crossChuHanBattlefieldPart.getDefList())) {
                if (team.getTeam() == null) {
                    if (team.getSoldierType() == eTongBingSoldierType.COMMON_SOLDIER.getValue()) {
                        cityDefTeamNum++;
                    } else if (team.getSoldierType() == eTongBingSoldierType.EXTRA_SOLDIER.getValue()) {
                        cityExtraDefTeamNum++;
                    }
                }
            }
        }


        ChuHanProto.ChuHanBattlefieldMsg.Builder builder = ChuHanProto.ChuHanBattlefieldMsg.newBuilder();
        builder.setPartId(part.getPartId());
        int defNo = part.getDefDeadCount();
        for (CrossChuHanBattlefieldTeam team : new LinkedList<>(part.getDefList())) {
            builder.addDef(parseChuHanBattlefieldTeamMsg(city, team, ++defNo));
        }
        int atkNo = part.getAtkDeadCount();
        for (CrossChuHanBattlefieldTeam team : new LinkedList<>(part.getAtkList())) {
            builder.addAtk(parseChuHanBattlefieldTeamMsg(city, team, ++atkNo));
        }

        int rankNum = 0;
        for (CrossChuHanBattlefieldRank rank : new ArrayList<>(battlefield.getBattlefieldRankNewList())) {
            if (rankNum++ > 5) {
                break;
            }
            builder.addRank(parseChuHanBattlefieldRankMsg(rank));
        }

        builder.setBattleNo(city.getBattledNum() + 1);
        builder.setBattleTime(battlefield.getCreateTime() + config.getBattlePrepareTime());
        builder.setDrumTimes(battlefield.getDrumTimes());
        builder.addAllPartList(battlefield.getPartMap().keySet());
        builder.setCityId(city.getCityId());
        builder.setWinType(part.getWinType());
        builder.setDefNum(defNum);
        builder.setAtkNum(atkNum);
        builder.setCityDefTeamNum(cityDefTeamNum);
        builder.setCityExtraDefTeamNum(cityExtraDefTeamNum);
        return builder;
    }

    public ChuHanProto.ChuHanBattlefieldRankMsg.Builder parseChuHanBattlefieldRankMsg(CrossChuHanBattlefieldRank rank) {
        ChuHanProto.ChuHanBattlefieldRankMsg.Builder builder = ChuHanProto.ChuHanBattlefieldRankMsg.newBuilder();
        builder.setUser(parseChuHanUserMsg(rank.getUserId()));
        builder.setValue(rank.getZhanGong());
        builder.setKillNum(rank.getKillNum());
        return builder;
    }

    public ChuHanProto.ChuHanBattlefieldTeamMsg.Builder parseChuHanBattlefieldTeamMsg(CrossChuHanCityData city, CrossChuHanBattlefieldTeam battlefieldTeam, int no) {
        ChuHanProto.ChuHanBattlefieldTeamMsg.Builder builder = ChuHanProto.ChuHanBattlefieldTeamMsg.newBuilder();
        builder.setTeamNo(no);

        if (battlefieldTeam.getTeam() != null) {
            CrossChuHanTeamArrive arrive = battlefieldTeam.getTeam();
            CrossChuHanTeam team = getTeamByArrive(arrive);
            CrossChuHanUserData userData = getUserData(arrive.getUserId());
            int patronsId = team.getPatronsList().isEmpty() ? 0 : team.getPatronsList().get(0);
            int skinId = userData.getPatronsMap().containsKey(patronsId) ? userData.getPatronsMap().get(patronsId).getSkin() : 0;

            builder.setUser(parseChuHanUserMsg(team.getUserId()));
            builder.setSoldierNum(Math.max(team.getSoldierNum(), 1)); // 战场内最少1个兵，不然客户端显示有问题
            builder.setPatronsId(patronsId);
            builder.setSkinId(skinId);
            builder.setSoldierMaxNum(team.getMaxSoldier());
        } else {
            // builder.setUser();
            builder.setSoldierNum(Math.max(battlefieldTeam.getSoldierNum(), 1)); // 战场内最少1个兵，不然客户端显示有问题
            builder.setPatronsId(0);
            builder.setSkinId(0);
            builder.setSoldierMaxNum(city.getTeamSoldierNum());
        }

        return builder;
    }

    public ChuHanProto.ChuHanUserMsg.Builder parseChuHanUserMsg(long userId) {
        CrossChuHanUserData userData = getUserData(userId);
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        String nickname = userBaseInfo == null ? "" : userBaseInfo.getNickName();

        ChuHanProto.ChuHanUserMsg.Builder builder = ChuHanProto.ChuHanUserMsg.newBuilder();
        builder.setUserId(userId);
        builder.setCampId(userData.getCampId());
        builder.setNickname(nickname);
        return builder;
    }

    public ChuHanProto.ChuHanCampRankMsg.Builder parseChuHanCampRankMsg(CrossChuHanRank rank) {
        Integer campId = Integer.valueOf(rank.getId());

        // 拿一下阵营第一的昵称
        UserBaseInfo userBaseInfo = getCampMaxZhanGongUserBaseInfo(campId);
        String nickname = userBaseInfo == null ? "" : userBaseInfo.getNickName();

        ChuHanProto.ChuHanCampRankMsg.Builder builder = ChuHanProto.ChuHanCampRankMsg.newBuilder();
        builder.setCampId(campId);
        builder.setValue(rank.getValue());
        builder.setMaxNickname(nickname);
        return builder;
    }

    public ChuHanProto.ChuHanCampUnionRankMsg.Builder parseChuHanCampUnionRankMsg(CrossChuHanRank rank) {
        String unionUid = rank.getId();
        CrossChuHanUnionJoin crossChuHanUnionJoin = unionJoinMap.get(unionUid);
        UnionBaseInfo unionBaseInfo = crossChuHanUnionJoin == null ? new UnionBaseInfo() : crossChuHanUnionJoin.getUnionBaseInfo();
        ChuHanProto.ChuHanCampUnionRankMsg.Builder builder = ChuHanProto.ChuHanCampUnionRankMsg.newBuilder();
        builder.setUnionUid(unionUid);
        builder.setValue(rank.getValue());
        builder.setUnionInfo(UnionBasePb.parseUnionBaseTempMsg(unionBaseInfo));
        builder.setCampId(crossChuHanUnionJoin == null ? 0 : crossChuHanUnionJoin.getCampId());
        return builder;
    }

    public ChuHanProto.ChuHanUserRankMsg.Builder parseChuHanUserRankMsg(CrossChuHanRank rank) {
        Long userId = Long.valueOf(rank.getId());
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        if (userBaseInfo == null) {
            userBaseInfo = new UserBaseInfo();
        }
        int campId = getUserData(userId).getCampId();

        ChuHanProto.ChuHanUserRankMsg.Builder builder = ChuHanProto.ChuHanUserRankMsg.newBuilder();
        builder.setUserId(userId);

        //拿一下玩家锁定的商会id和商会名称
        PlayerProto.PlayerBaseTempMsg.Builder playerBaseTempMsg = PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo);
        String unionUid = Cross2UnionActivityGroupMgr.getUnionUid(getConfig().getActivityInfo().getActivityId(), userId);
        playerBaseTempMsg.setUnionUid(unionUid);
        UnionActivityGroupUnion unionData = Cross2UnionActivityGroupMgr.getUnionData(getConfig().getActivityInfo().getActivityId(), unionUid);
        if (unionData != null) {
            playerBaseTempMsg.setUnionName(unionData.getUnionBaseInfo().getUnionName());
        }

        builder.setPlayerBaseData(playerBaseTempMsg);
        builder.setValue(rank.getValue());
        builder.setCampId(campId);
        return builder;
    }

    public ChuHanProto.ChuHanRedPacketMsg.Builder parseChuHanRedPacketMsg(CrossChuHanRedPacket redPacket, boolean getReward) {
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(redPacket.getUserId());
        if (userBaseInfo == null) {
            userBaseInfo = new UserBaseInfo();
        }

        ChuHanProto.ChuHanRedPacketMsg.Builder tempMsg = ChuHanProto.ChuHanRedPacketMsg.newBuilder();
        tempMsg.setUserId(redPacket.getUserId());
        tempMsg.setNickName(userBaseInfo.getNickName());
        tempMsg.setUnionName(userBaseInfo.getUnionName());
        tempMsg.setType(redPacket.getGoodId());
        tempMsg.setOrderNo("" + redPacket.getRedPacketNo());
        tempMsg.setIsGet(getReward ? 1 : 0);
        return tempMsg;
    }

    // ============================== 聊天 ==============================

    /**
     * 添加系统聊天通知
     *
     * @param sendTime 毫秒，进来再转秒
     */
    private void addSystemChat(eMessageType messageType, int id, String content, long sendTime) {
        addSystemChat(messageType, null, id, content, sendTime);
    }

    /**
     * 添加系统聊天通知
     *
     * @param sendTime 毫秒，进来再转秒
     */
    private void addSystemChat(eMessageType messageType, Long userId, int id, String content, long sendTime) {
        // 转换秒
        sendTime = sendTime / 1000;

        int range = 0;
        if (messageType == eMessageType.ChuHanPublicBattleChat) {
            range = 1;
        } else if (messageType == eMessageType.ChuHanCampHelpChat) {
            range = 2;
        } else if (messageType == eMessageType.ChuHanBattlefieldWinChat) {
            range = 3;
        }

        if (range <= 0) {
            return;
        }

        WorldMessageProto.WorldMessageTempMsg.Builder builder = WorldMessageProto.WorldMessageTempMsg.newBuilder();
        builder.setType(messageType.getValue());
        builder.setSendTime(sendTime);
        builder.setContent(content);
        builder.setActivityId(config.getActivityInfo().getActivityId());
        if (userId != null) {
            UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
            if (userBaseInfo != null) {
                builder.setUserId(userId);
                builder.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
            }
        }
        chatLogic.chat(range, id, 0, builder.build());
    }

    public void syncChat(int range, int id, WorldMessageProto.WorldMessageTempMsg chatMsg) {
        long now = System.currentTimeMillis();
        List<Long> userIdList = new ArrayList<>();
        if (range == CrossChuHanChatLogic.RANGE_ALL) {
            userIdList = userDataMap.values().stream().filter(item -> isInActivity(item, now)).mapToLong(item -> item.getUserId()).boxed().collect(Collectors.toList());
        } else if (range == CrossChuHanChatLogic.RANGE_CAMP) {
            userIdList = userDataMap.values().stream().filter(item -> item.getCampId() == id && isInActivity(item, now)).mapToLong(item -> item.getUserId()).boxed().collect(Collectors.toList());
        } else if (range == CrossChuHanChatLogic.RANGE_BATTLEFIELD) {
            CrossChuHanBattlefield battlefield = getCrossChuHanData().getCityMap().get(id).getBattlefield();
            if (battlefield != null) {
                for (Integer partId : battlefield.getPartMap().keySet()) {
                    List<Long> list = CrossSceneMgr.getSceneUserList(config.getActivityInfo().getActivityId(),
                                    eSceneType.TongBingBattlefield.getValue(),
                                    getBattlefieldSceneKey(id, partId),
                                    23333)
                            .stream().mapToLong(item -> item.getUserId()).boxed().collect(Collectors.toList());
                    userIdList.addAll(list);
                }
            }
        }
        if (!userIdList.isEmpty()) {
            sendPacketToAll(ClientProtocol.U_WORLD_MESSAGE_SYNC, chatMsg.toBuilder(), userIdList);
        }
    }

    // ============================== 其他 ==============================

    public void save() {
        CrossCommonActivityDataDaoImpl.save(crossChuHanData);

        for (CrossChuHanCityData cityData : crossChuHanData.getCityMap().values()) {
            CrossCommonActivityDataDaoImpl.save(cityData);
        }

        for (CrossChuHanUnionJoin join : unionJoinMap.values()) {
            CrossCommonActivityDataDaoImpl.save(join);
        }

        for (CrossChuHanUserData data : userDataMap.values()) {
            CrossCommonActivityDataDaoImpl.save(data);
        }

        List<CrossChuHanCityWarReport> tempNeedInsertCityReportList;
        synchronized (lock) {
            tempNeedInsertCityReportList = new ArrayList<>(needInsertCityReportList);
            needInsertCityReportList.clear();
        }
        if (tempNeedInsertCityReportList.size() > 0) {
            new CrossCommonActivityDataDaoImpl<CrossChuHanCityWarReport>().batchAdd(CrossChuHanCityWarReport.class, tempNeedInsertCityReportList);
        }

        List<CrossChuHanUserWarReport> tempNeedInsertUserReportList;
        synchronized (lock) {
            tempNeedInsertUserReportList = new ArrayList<>(needInsertUserReportList);
            needInsertUserReportList.clear();
        }
        if (tempNeedInsertUserReportList.size() > 0) {
            new CrossCommonActivityDataDaoImpl<CrossChuHanUserWarReport>().batchAdd(CrossChuHanUserWarReport.class, tempNeedInsertUserReportList);
        }

        for (CrossChuHanCampData campData : campDataMap.values()) {
            CrossCommonActivityDataDaoImpl.save(campData);
        }
    }

    /**
     * 玩家是否在界面内
     */
    public boolean isInActivity(CrossChuHanUserData data, long now) {
        if (data == null) {
            return false;
        }
        if (now - data.getLastReqTime() < DateHelper.MINUTE_MILLIONS * 3) {
            return true; // 3分钟内有请求的都当他在线
        }
        return false;
    }

    /**
     * 设置请求时间
     */
    public void setLastReqTime(long userId) {
        if (userId <= 0) {
            return;
        }
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        userData.setLastReqTime(System.currentTimeMillis());
    }

    public void printTask() {
        Map<String, Integer> countMap = new ConcurrentHashMap<>();
        for (BaseCrossChuHanTask task : taskQueue) {
            countMap.put(task.getType().name(), countMap.getOrDefault(task.getType().name(), 0) + 1);
        }
        logger.info("【楚汉争霸任务】 - {}", JSON.toJSONString(countMap));
    }

    public String getBattlefieldSceneKey(int cityId, int partId) {
        return groupId + "-" + cityId + "-" + partId;
    }

    public void syncNeedSyncTeam() {
        // 拿数据
        List<String> teamKeyList;
        synchronized (needSyncTeam) {
            teamKeyList = new ArrayList<>(needSyncTeam);
            needSyncTeam.clear();
        }
        // 没数据不同步
        if (teamKeyList.isEmpty()) {
            return;
        }
        // 拿在游戏内玩家
        long now = System.currentTimeMillis();
        List<Long> userList = userDataMap.values().stream().filter(item -> isInActivity(item, now)).map(item -> item.getUserId()).collect(Collectors.toList());
        if (userList.isEmpty()) {
            return;
        }
        // 转化格式
        List<CrossChuHanTeam> teamList = new ArrayList<>();
        for (String teamKey : teamKeyList) {
            teamList.add(getTeamByKey(teamKey));
        }
        // 同步
        ChuHanProto.ChuHanSyncTeamRespMsg.Builder builder = ChuHanProto.ChuHanSyncTeamRespMsg.newBuilder();
        for (CrossChuHanTeam team : teamList) {
            builder.addTeam(parseChuHanTeamMsg(getUserData(team.getUserId()), team));
        }
        sendPacketToAll(Protocol.S_CHUHAN_SYNC_TEAM % 20000, builder, userList);
    }

    public void syncNeedSyncBarracks() {
        List<Long> userList;
        synchronized (needSyncBarracksUsers) {
            userList = new ArrayList<>(needSyncBarracksUsers);
            needSyncBarracksUsers.clear();
        }

        long now = System.currentTimeMillis();
        for (Long userId : userList) {
            CrossChuHanUserData userData = getUserData(userId);
            if (userData == null) {
                continue;
            }
            if (!isInActivity(userData, now)) {
                continue;
            }
            syncBarracksMsg(userId);
        }

    }

    public void syncNeedSyncBaseUserData() {
        List<Long> userList;
        synchronized (needSyncDataUsers) {
            userList = new ArrayList<>(needSyncDataUsers);
            needSyncDataUsers.clear();
        }

        for (Long userId : userList) {
            syncUserDataToGameServer(getUserData(userId));
        }
    }

    public void addNeedSyncTeam(CrossChuHanTeam team) {
        String key = getTeamKey(team);
        synchronized (needSyncTeam) {
            needSyncTeam.add(key);
            // 20个就提前同步
            if (needSyncTeam.size() > 20) {
                syncNeedSyncTeam();
            }
        }
    }

    public void addNeedSyncBarracksUser(long userId) {
        synchronized (needSyncBarracksUsers) {
            needSyncBarracksUsers.add(userId);
        }
    }

    public void addNeedSyncBaseUser(long userId) {
        synchronized (needSyncDataUsers) {
            needSyncDataUsers.add(userId);
        }
    }

    public void addNeedSyncCity(int cityId) {
        synchronized (needSyncCity) {
            needSyncCity.add(cityId);
            // 20个就提前同步
            if (needSyncCity.size() > 20) {
                syncNeedSyncCity();
            }
        }
    }

    public void syncNeedSyncCity() {
        // 拿数据
        List<Integer> cityIdList;
        synchronized (needSyncCity) {
            cityIdList = new ArrayList<>(needSyncCity);
            needSyncCity.clear();
        }
        // 没数据不同步
        if (cityIdList.isEmpty()) {
            return;
        }
        // 拿在游戏内玩家
        long now = System.currentTimeMillis();
        List<Long> userList = userDataMap.values().stream().filter(item -> isInActivity(item, now)).map(item -> item.getUserId()).collect(Collectors.toList());
        if (userList.isEmpty()) {
            return;
        }
        // 转化格式
        List<CrossChuHanCityData> cityList = new ArrayList<>();
        Map<Integer, CrossChuHanCityData> cityMap = getCrossChuHanData().getCityMap();
        for (int cityId : cityIdList) {
            cityList.add(cityMap.get(cityId));
        }
        // 同步
        ChuHanProto.ChuHanSyncCityRespMsg.Builder builder = ChuHanProto.ChuHanSyncCityRespMsg.newBuilder();
        for (CrossChuHanCityData city : cityList) {
            builder.addCity(parseChuHanCityMsg(city));
        }
        sendPacketToAll(Protocol.S_CHUHAN_SYNC_CITY % 20000, builder, userList);
    }

    public String getTeamKey(CrossChuHanTeam team) {
        return team.getUserId() + "-" + team.getOccupation();
    }

    public String getTeamKey(CrossChuHanTeamArrive arrive) {
        return arrive.getUserId() + "-" + arrive.getTeamId();
    }

    public CrossChuHanTeam getTeamByKey(String key) {
        String[] split = key.split("-");
        CrossChuHanUserData userData = getUserData(Long.valueOf(split[0]));
        return userData.getTeamMap().get(Integer.valueOf(split[1]));
    }

    public List<Long> getBattlefieldUserIdList(int cityId, int partId) {
        String sceneKey = getBattlefieldSceneKey(cityId, partId);
        List<UserScene> sceneUserList = CrossSceneMgr.getSceneUserList(
                config.getActivityInfo().getActivityId(),
                eSceneType.TongBingBattlefield.getValue(),
                sceneKey,
                23333);
        return sceneUserList.stream().mapToLong(UserScene::getUserId).boxed().collect(Collectors.toList());
    }

    /**
     * 领取榜单奖励
     */
    private int getRankReward(long userId, int conditionType) {
        CrossChuHanUserData userData = getUserData(userId);

        // 资格
        if (!isAble(userId)) {
            return GameErrorCode.E_CHUHAN_NO_ABLE;
        }

        // 拿职位参数
        CrossChuHanUnionJoin join = unionJoinMap.get(userData.getUnionUid());
        CrossChuHanUnionJoinMember member = join.getMemberMap().get(userId);
        int position = Cross2UnionActivityGroupMgr.getUnionPosition(userData.getActivityId(), userData.getUserId());

        // 拿阵营排名参数
        LinkedList<CrossChuHanRank> campRankList = getCampScoreRankList();
        String myCampId = String.valueOf(userData.getCampId());
        int myCampRank = -1;
        int campRankNum = 0;
        for (CrossChuHanRank rank : campRankList) {
            campRankNum++;
            if (Objects.equals(rank.getId(), myCampId)) {
                myCampRank = campRankNum;
                break;
            }
        }

        // 拿榜单
        String myId;
        LinkedList<CrossChuHanRank> rankList;
        if (conditionType == eGamePlayerEventType.ChuHanUserZhanGongRank.getValue()) {
            rankList = userRank.getRankList();
            myId = String.valueOf(userId);
        } else if (conditionType == eGamePlayerEventType.ChuHanCampUnionZhanGongRank.getValue()) {
            CrossChuHanRankListModel unionRank = campUnionRankMap.get(userData.getCampId());
            rankList = unionRank == null ? new LinkedList<>() : unionRank.getRankList();
            myId = userData.getUnionUid();
        } else if (conditionType == eGamePlayerEventType.ChuHanCampScoreRank.getValue()) {
            rankList = campRankList;
            myId = String.valueOf(userData.getCampId());
        } else {
            return GameErrorCode.E_CHUHAN_PARAM_ERR;
        }

        // 【拿榜单排名】
        int myRank = -1;
        int num = 0;
        for (CrossChuHanRank rank : rankList) {
            num++;
            if (Objects.equals(rank.getId(), myId)) {
                myRank = num;
                break;
            }
        }

        ChuHanProto.CrossChuHanGetRankRewardRespMsg.Builder builder = ChuHanProto.CrossChuHanGetRankRewardRespMsg.newBuilder();
        builder.setRet(0);
        builder.setActivityId(config.getActivityInfo().getActivityId());
        builder.setConditionType(conditionType);
        builder.setPosition(position);
        builder.setRank(myRank);
        builder.setCampRank(myCampRank);
        builder.setMyId(myId);
        sendPacket(GameProtocol.S_CHUHAN_GET_RANK_REWARD_FROM_CROSS, builder, userId);
        return 0;
    }

    /**
     * 通知游戏服condition更新值
     */
    private void notifyConditionChange(long userId, eGamePlayerEventType type, long value) {
        ChuHanProto.CrossChuHanNotifyConditionRespMsg.Builder builder = ChuHanProto.CrossChuHanNotifyConditionRespMsg.newBuilder();
        builder.setUserId(userId);
        builder.setConditionType(type.getValue());
        builder.setValue(value);
        sendPacket(GameProtocol.S_CHUHAN_NOTIFY_CONDITION_FROM_CROSS, builder, userId);
    }

    public CrossChuHanRankListModel getUserRank() {
        return userRank;
    }

    public Map<Integer, CrossChuHanRankListModel> getCampUnionRankMap() {
        return campUnionRankMap;
    }

    public Map<String, CrossChuHanUnionJoin> getUnionJoinMap() {
        return unionJoinMap;
    }

    /**
     * 同步游戏内顶部消息
     *
     * @param type        1攻占成功，2防守成功
     * @param battlefield 可以为空
     */
    public void syncTopMsg(int campId, int cityId, int type, CrossChuHanBattlefield battlefield, int defCampId) {

        // 拿在界面内玩家
        long now = System.currentTimeMillis();
        Set<Long> userIdSet = new HashSet<>(
                userDataMap.values().stream().filter(item -> isInActivity(item, now)).map(item -> item.getUserId()).collect(Collectors.toList())
        );

        // 战场兼容null
        if (battlefield != null) {
            // 有战功的单独同步
            for (CrossChuHanBattlefieldRank rank : battlefield.getBattlefieldRankNewList()) {
                if (rank.getZhanGong() > 0 && userIdSet.contains(rank.getUserId())) {

                    ChuHanProto.ChuHanSyncTopMsgRespMsg.Builder builder = ChuHanProto.ChuHanSyncTopMsgRespMsg.newBuilder();
                    builder.setCampId(campId);
                    builder.setCityId(cityId);
                    builder.setType(type);
                    builder.setGetZhanGong(rank.getZhanGong());
                    builder.setDefCampId(defCampId);
                    sendPacket(Protocol.S_CHUHAN_SYNC_TOP_MSG % 20000, builder, rank.getUserId());

                    // 记得移除单独同步玩家
                    userIdSet.remove(rank.getUserId());
                }
            }
        }

        // 其余的批量同步
        ChuHanProto.ChuHanSyncTopMsgRespMsg.Builder builder = ChuHanProto.ChuHanSyncTopMsgRespMsg.newBuilder();
        builder.setCampId(campId);
        builder.setCityId(cityId);
        builder.setType(type);
        builder.setGetZhanGong(0);
        builder.setDefCampId(defCampId);
        sendPacketToAll(Protocol.S_CHUHAN_SYNC_TOP_MSG % 20000, builder, userIdSet);
    }

    public void changeHorse(long userId, int horseId) {
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        synchronized (lock) {

            if (horseId <= userData.getHorseId()) {
                logger.info("tong bing change horse fail! activityId {} userId {} horseId {} newHorseId {}.",
                        config.getActivityInfo().getActivityId(), userData.getUserId(), userData.getHorseId(), horseId);
                return;
            }

            // 换马
            userData.setHorseId(horseId);

            // 没在活动时间内
            long now = System.currentTimeMillis();
            if (now < config.getStage1TimeList().get(0).get(0) || now > config.getActivityInfo().getEndTime() * 1000L) {
                return;
            }

            // 同步
            for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                if (getTeamStatus(team) == eTongBingTeamStatus.Move) {
                    addNeedSyncTeam(team);
                }
            }
        }
    }

    /**
     * 邮件补发辎重
     */
    public void mailSendCityCollect() {

        // 没到领奖期，不发
        if (System.currentTimeMillis() < config.getActivityInfo().getEndTime() * 1000) {
            return;
        }

        synchronized (lock) {

            // 已经发过
            if (getCrossChuHanData().getAutoSendCollect() == 1) {
                return;
            }

            // 重新计算辎重
            for (int campId = 1; campId <= config.getCampNum(); campId++) {
                makeAllCityCollect(campId, config.getActivityInfo().getEndTime() * 1000L);
            }

            // 统计全部玩家数据
            Map<Long, Map<Long, Property>> serverUserRewardMap = new ConcurrentHashMap<>();
            for (CrossChuHanUserData userData : userDataMap.values()) {
                long userId = userData.getUserId();
                Property reward = new Property();
                // 拿数量
                long allFood = userData.getAllFood();
                //清空
                userData.setFoodMap(new ConcurrentHashMap<>());

                if (allFood > 0) {
                    reward.addProperty(new Property(getConfig().getZiZhongItem(), allFood));
                }
                int allBingFu = userData.getAllBingFu();
                //清空
                userData.setBingFuMap(new ConcurrentHashMap<>());
                if (allBingFu > 0) {
                    reward.addProperty(new Property(getConfig().getBingFuItem(), allBingFu));
                }

                //PVE给的虎符
                {
                    int pveCityType = eChuHanCityType.Type7_BeiJing.getValue();
                    if (getConfig().needShowPveResult(pveCityType, userData.getReqPveResultTime())) {
                        CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
                        int pveBuff = campData.getPveBuff(pveCityType);
                        if (pveBuff > 0) {
                            ChuHanBuffTypeConfig buffTypeConfig = getConfig().getBuffTypeConfigMap().get(pveBuff);
                            if (buffTypeConfig != null && buffTypeConfig.getType() == eChuHanBuffType.Type12_BingFuReward.getValue()) {
                                reward.addProperty(new Property(getConfig().getBingFuItem(), buffTypeConfig.getBuffParamsByLevel(1).get(0)));
                            }
                        }
                    }
                }

                {
                    int pveCityType = eChuHanCityType.Type8_NanYang.getValue();
                    if (getConfig().needShowPveResult(pveCityType, userData.getReqPveResultTime())) {
                        CrossChuHanCampData campData = campDataMap.get(userData.getCampId());
                        int pveBuff = campData.getPveBuff(pveCityType);
                        if (pveBuff > 0) {
                            ChuHanBuffTypeConfig buffTypeConfig = getConfig().getBuffTypeConfigMap().get(pveBuff);
                            if (buffTypeConfig != null && buffTypeConfig.getType() == eChuHanBuffType.Type12_BingFuReward.getValue()) {
                                reward.addProperty(new Property(getConfig().getBingFuItem(), buffTypeConfig.getBuffParamsByLevel(1).get(0)));
                            }
                        }
                    }
                }
                userData.setReqPveResultTime(System.currentTimeMillis());

                // 统计值
                if (!reward.isNothing()) {
                    UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
                    if (userBaseInfo == null) {
                        logger.info("mailSendCityCollect can not find userInfo. activityId {} userId {} reward {}.",
                                config.getActivityInfo().getActivityId(), userId, reward.toHelperString());
                    } else {
                        serverUserRewardMap.putIfAbsent(userBaseInfo.getServerId(), new ConcurrentHashMap<>());
                        serverUserRewardMap.get(userBaseInfo.getServerId()).put(userId, reward);
                    }
                }
            }

            // 日志
            logger.debug("activityId {} , mailSendCityCollect : {}",
                    config.getActivityInfo().getActivityId(), JSON.toJSONString(serverUserRewardMap));

            // 改状态
            getCrossChuHanData().setAutoSendCollect(1);
            for (CrossChuHanCampData campData : campDataMap.values()) {
                campData.setUpdateOption();
            }

            // 按区服发奖
            for (Map.Entry<Long, Map<Long, Property>> entry : serverUserRewardMap.entrySet()) {
                Long serverId = entry.getKey();
                ChuHanProto.CrossChuHanMailSendCityCollectReqMsg.Builder builder = ChuHanProto.CrossChuHanMailSendCityCollectReqMsg.newBuilder();
                for (Map.Entry<Long, Property> rewardEntry : entry.getValue().entrySet()) {
                    long userId = rewardEntry.getKey();
                    ChuHanProto.CrossChuHanCityCollectMsg.Builder tmp = ChuHanProto.CrossChuHanCityCollectMsg.newBuilder();
                    tmp.setUserId(userId);
                    tmp.setReward(rewardEntry.getValue().toHelperString());
                    builder.addList(tmp);
                }
                YanQuMessage message = YanQuMessageUtils.buildMessage(GameProtocol.S_CHUHAN_AUTO_SEND_CITY_COLLECT, builder);
                MessageHelper.sendPacket(serverId, 0, message);
            }

        }

        //活动结束,这里更新下职位
        for (CrossChuHanUnionJoin unionJoin : unionJoinMap.values()) {
            for (CrossChuHanUnionJoinMember member : unionJoin.getMemberMap().values()) {
                int position = Cross2UnionActivityGroupMgr.getUnionPosition(getConfig().getActivityInfo().getActivityId(), member.getUserId());
                member.setPosition(position);
            }
            unionJoin.setUpdateOption();
        }

        setAllRank();
    }

    /**
     * //把结算的排名入库
     */
    private void setAllRank() {
        LinkedList<CrossChuHanRank> rankList = userRank.getRankList();
        for (int i = 0; i < rankList.size(); i++) {
            int rank = i + 1;
            CrossChuHanRank crossChuHanRank = rankList.get(i);
            CrossChuHanUserData userData = getUserData(Long.parseLong(crossChuHanRank.getId()));
            if (userData == null) {
                continue;
            }
            userData.setUserRank(rank);
        }

        for (Map.Entry<Integer, CrossChuHanRankListModel> entry : campUnionRankMap.entrySet()) {
            int campId = entry.getKey();
            CrossChuHanRankListModel rankListModel = entry.getValue();
            LinkedList<CrossChuHanRank> unionRankList = rankListModel.getRankList();
            for (int i = 0; i < unionRankList.size(); i++) {
                int rank = i + 1;
                CrossChuHanRank crossChuHanRank = unionRankList.get(i);
                CrossChuHanUnionJoin unionJoin = getUnionJoin(crossChuHanRank.getId());
                if (unionJoin == null) {
                    continue;
                }
                unionJoin.setCampRank(rank);
            }
        }

        LinkedList<CrossChuHanRank> campScoreRankList = getCampScoreRankList();
        for (int i = 0; i < campScoreRankList.size(); i++) {
            int rank = i + 1;
            CrossChuHanRank crossChuHanRank = campScoreRankList.get(i);
            int campId = Integer.parseInt(crossChuHanRank.getId());
            CrossChuHanCampData campData = campDataMap.get(campId);
            if (campData == null) {
                continue;
            }
            campData.setRank(rank);
            campData.setRankScore(crossChuHanRank.getValue());

            CrossAutoLogMgr.add(new LogChuHanCampRank(
                    config.getActivityInfo().getActivityId(),
                    groupId,
                    campId,
                    rank,
                    campData.getRankScore()
            ));
        }
    }

    /**
     * 获取阵营最高战功的玩家
     */
    private UserBaseInfo getCampMaxZhanGongUserBaseInfo(int campId) {
        long userId = getCampMaxZhanGongUserId(campId);
        return userId == 0 ? null : CrossUserMgr.getUserBaseInfo(userId);
    }

    /**
     * 获取阵营最高战功的玩家Id
     */
    private long getCampMaxZhanGongUserId(int campId) {
        long userId = 0;
        CrossChuHanRankListModel rankListModel = campUserRankMap.get(campId);
        if (rankListModel != null) {
            LinkedList<CrossChuHanRank> rankList = rankListModel.getRankList();
            if (!rankList.isEmpty()) {
                CrossChuHanRank first = rankList.getFirst();
                userId = Long.parseLong(first.getId());
            }
        }
        return userId;
    }

    public long getUnionZhanGong(String unionUid) {
        CrossChuHanUnionJoin unionJoin = getUnionJoin(unionUid);
        if (unionJoin == null) {
            return 0;
        }

        long totalZhanGong = 0;
        for (CrossChuHanUnionJoinMember joinMember : unionJoin.getMemberMap().values()) {
            CrossChuHanUserData userData = getUserData(joinMember.getUserId());
            if (userData == null) {
                continue;
            }
            totalZhanGong += userData.getZhanGong();
        }
        return totalZhanGong;
    }

    //传入最低排名获得前几名商会Uid
    private List<String> getTopUnions(int lowestRewardRank) {
        List<CrossChuHanRank> rankList = new ArrayList<>();
        for (CrossChuHanRankListModel model : campUnionRankMap.values()) {
            if (model != null) {
                rankList.addAll(model.getRankList());
            }
        }
        rankList.sort((o1, o2) -> {
            Long num1 = o1.getValue();
            Long num2 = o2.getValue();
            return num2.compareTo(num1);
        });

        List<String> unionUidList = new ArrayList<>();
        //找出top n的Uid 按照排名顺序塞入List
        for (int num = 0; num < lowestRewardRank && num < rankList.size(); num++) {
            unionUidList.add(rankList.get(num).getId());
        }
        return unionUidList;
    }


    public List<ChuHanMapNode> generalShortestPath(int startCamp, ChuHanMapGraph graph, int startId, int targetId) {
        ChuHanMapNode startChuHanMapNode = graph.getNode(startId);
        ChuHanMapNode targetChuHanMapNode = graph.getNode(targetId);

        if (startChuHanMapNode == null || targetChuHanMapNode == null) {
            return Collections.emptyList();
        }

        Map<ChuHanMapNode, Integer> distances = new HashMap<>();
        Map<ChuHanMapNode, ChuHanMapNode> predecessors = new HashMap<>(); // 记录前驱节点
        PriorityQueue<ChuHanMapNodeDistance> pq = new PriorityQueue<>(Comparator.comparingInt(nd -> nd.distance));

        for (ChuHanMapNode node : graph.getNodes().values()) {
            distances.put(node, Integer.MAX_VALUE);
        }
        distances.put(startChuHanMapNode, 0);
        pq.offer(new ChuHanMapNodeDistance(startChuHanMapNode, 0));

        while (!pq.isEmpty()) {
            ChuHanMapNodeDistance current = pq.poll();

            if (current.node.equals(targetChuHanMapNode)) {
                return buildPath(predecessors, startChuHanMapNode, targetChuHanMapNode);
            }

            if (current.distance > distances.get(current.node)) {
                continue;
            }

            for (ChuHanMapNode neighbor : current.node.getAdjacent()) {
                int weight = getBetweenNodeWeight(startCamp, current.node, neighbor);
                int distance = current.distance + weight;

                if (distance < distances.get(neighbor)) {
                    distances.put(neighbor, distance);
                    predecessors.put(neighbor, current.node); // 更新前驱节点
                    pq.offer(new ChuHanMapNodeDistance(neighbor, distance));
                }
            }
        }

        return Collections.emptyList(); // 表示没有路径
    }

    private int getBetweenNodeWeight(int startCamp, ChuHanMapNode startNode, ChuHanMapNode toNode) {
        int toCampId = getCityCamp(toNode.getCityId());
        ChuHanCityConfig fromCityConfig = getConfig().getCityConfigMap().get(startNode.getCityId());

        int notPassValue = 99999999;
        ChuHanCityConfig toCityConfig = getConfig().getCityConfigMap().get(toNode.getCityId());
        if (toCityConfig.getType() == eChuHanCityType.Type1_MainCity.getValue()) {
            //如果经过主城,不是自己阵营的不能去
            if (startCamp != toCampId) {
                return notPassValue;
            }
        } else if (toCityConfig.getType() == eChuHanCityType.Type6_CenterCity.getValue()) {
            //如果经过主城,不是自己阵营的不能去
            //去中心城的时间还没到
            if (System.currentTimeMillis() < config.getStage2BeginTime()) {
                return notPassValue;
            }
        } else if (getConfig().isPveCityByCityType(toCityConfig.getType())) {
            if (fromCityConfig.getMapCamp() != startCamp) {//不是自己阵营的PVE不能通过
                return notPassValue;
            }
            if (!getConfig().isPveCityOpenTime(toCityConfig.getType())) {//要去的是PVE城市,没开不让去
                return notPassValue;
            }
        }

        eChuHanCityStatus fromStatus = getCityStatus(startCamp, startNode.getCityId());
        eChuHanCityStatus toStatus = getCityStatus(toCampId, toNode.getCityId());
        return eChuHanCityStatus.getMoveWeight(fromStatus, toStatus);
    }

    private eChuHanCityStatus getCityStatus(int compareCamp, int cityId) {
        /**
         *     Type1_MyCampPeace(1, "我方无战斗"),
         *     Type2_MyCampBattle(2, "我方战斗中"),
         *     Type3_EnemyCampPeace(3, "敌方无战斗"),
         *     Type4_EnemyCampBattle(4, "敌方战斗中"),
         *     Type5_NotCampPeace(5, "系统阵营无战斗"),
         *     Type6_NotCampBattle(6, "系统阵营战斗中"),
         */
        CrossChuHanCityData city = crossChuHanData.getCityMap().get(cityId);
        if (city == null) {
            return eChuHanCityStatus.Type5_NotCampPeace;
        }

        int cityCamp = city.getCampId();
        if (cityCamp == compareCamp) {
            if (city.getBattlefield() == null) {
                return eChuHanCityStatus.Type1_MyCampPeace;
            } else {
                return eChuHanCityStatus.Type2_MyCampBattle;
            }
        } else if (cityCamp == 0) {
            if (city.getBattlefield() == null) {
                return eChuHanCityStatus.Type5_NotCampPeace;
            } else {
                return eChuHanCityStatus.Type6_NotCampBattle;
            }
        } else {
            if (city.getBattlefield() == null) {
                return eChuHanCityStatus.Type3_EnemyCampPeace;
            } else {
                return eChuHanCityStatus.Type4_EnemyCampBattle;
            }
        }
    }

    private int getCityCamp(int cityId) {
        CrossChuHanCityData city = crossChuHanData.getCityMap().get(cityId);
        if (city == null) {
            return 0;
        }
        return city.getCampId();
    }

    private List<ChuHanMapNode> buildPath(Map<ChuHanMapNode, ChuHanMapNode> predecessors, ChuHanMapNode startChuHanMapNode, ChuHanMapNode targetChuHanMapNode) {
        List<ChuHanMapNode> path = new ArrayList<>();
        ChuHanMapNode currentChuHanMapNode = targetChuHanMapNode;

        while (currentChuHanMapNode != null && !currentChuHanMapNode.equals(startChuHanMapNode)) {
            path.add(currentChuHanMapNode);
            currentChuHanMapNode = predecessors.get(currentChuHanMapNode);
        }

        if (currentChuHanMapNode == null) {
            return Collections.emptyList(); // 没有找到路径
        }

        path.add(startChuHanMapNode);
        Collections.reverse(path); // 反转路径以正确顺序显示
        return path;
    }

    private class ChuHanMapNodeDistance {
        ChuHanMapNode node;
        int distance;

        ChuHanMapNodeDistance(ChuHanMapNode node, int distance) {
            this.node = node;
            this.distance = distance;
        }
    }


    // ========= 预留方法 =========

    public void reloadConfig() {
        loadConfigFromDb(config.getActivityInfo());
    }

    public String hotCode(String param) {

        return "";
    }

    public String debugByCmd(String code, String param, long userId) {
        long nowTime = DateHelper.getCurrentTime();
        CrossChuHanUserData userData = getUserData(userId);
        if (userData == null) {
            return "";
        }
        if (code.equals("/chzbzhanling")) {
            int targetCityId = Integer.parseInt(param);
            int campId = userData.getCampId();
            for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                int fromCityId = team.getCityId();
                List<Integer> routeList = getRouteListNew(userId, fromCityId, targetCityId);
                if (routeList != null) {
                    for (Integer cityId : routeList) {
                        CrossChuHanCityData city = crossChuHanData.getCityMap().get(cityId);
                        city.setCampId(userData.getCampId());
                        city.setCollectionTime(nowTime);
                        city.setOccupyTime(nowTime);
                        checkUserBirthCity(city);

                        //更新阵营BUFF
                        if (getConfig().isCityHaveBuff(city.getCityId())) {
                            refreshCampBuff(campId);
                            isNeedSyncBuff.set(true);
                        }
                    }

                    team.setRouteList(Collections.singletonList(targetCityId));
                    forceArriveNextCity(team, nowTime);

                    for (Integer cityId : routeList) {
                        addNeedSyncCity(cityId);
                    }
                }
            }
        } else if (code.equals("/chzbsetcamp")) {
            List<Integer> list = StringUtils.stringToIntegerList(param, "=");
            int cityId = list.get(0);
            int camp = list.get(1);
            if (cityId > 0) {
                CrossChuHanCityData city = crossChuHanData.getCityMap().get(cityId);
                if (city != null) {
                    city.setCampId(camp);
                    city.setCollectionTime(nowTime);
                    city.setOccupyTime(nowTime);
                    checkUserBirthCity(city);

                    if (camp > 0) {
                        campOccupyCity(city, camp);
                        //更新阵营BUFF
                        if (getConfig().isCityHaveBuff(city.getCityId())) {
                            refreshCampBuff(camp);
                            isNeedSyncBuff.set(true);
                        }
                    }

                    addNeedSyncCity(cityId);
                }
            }else {
                //把所有城市,除了根据地和中心城PVE城都改成同一个阵营
                for (CrossChuHanCityData city : crossChuHanData.getCityMap().values()) {
                    ChuHanCityConfig cityConfig = config.getCityConfigMap().get(city.getCityId());
                    if (cityConfig.getType() == eChuHanCityType.Type1_MainCity.getValue()) {
                        continue;
                    }
                    if (cityConfig.getType() == eChuHanCityType.Type6_CenterCity.getValue()) {
                        continue;
                    }
                    if (cityConfig.getType() == eChuHanCityType.Type7_BeiJing.getValue()) {
                        continue;
                    }
                    if (cityConfig.getType() == eChuHanCityType.Type8_NanYang.getValue()) {
                        continue;
                    }
                    if (city.getCampId() != camp) {
                        city.setCampId(camp);
                        city.setCollectionTime(nowTime);
                        city.setOccupyTime(nowTime);
                        checkUserBirthCity(city);

                        if (camp > 0) {
                            campOccupyCity(city, camp);
                            //更新阵营BUFF
                            if (getConfig().isCityHaveBuff(city.getCityId())) {
                                refreshCampBuff(camp);
                                isNeedSyncBuff.set(true);
                            }
                        }

                        addNeedSyncCity(cityId);
                    }
                }
            }


        } else if (code.equals("/chzbroutlist")) {
            List<Integer> list = StringUtils.stringToIntegerList(param, "=");
            int cityId = list.get(0);
            int targetCityId = list.get(1);
            List<Integer> pathList = getRouteListNew(userId, cityId, targetCityId);
            List<String> nameList = new ArrayList<>();
            for (Integer pathId : pathList) {
                ChuHanCityConfig cityConfig = getConfig().getCityConfigMap().get(pathId);
                nameList.add(cityConfig.getName());
            }
            String content = "【路径】" + StringUtils.listToString(pathList, "->");
            content += "\n【城市】" + StringUtils.listToString(nameList, "->");
            WorldMessageProto.WorldMessageTempMsg.Builder msg = parseChatMsg(eMessageType.ChuHanPublicChat.getValue(), userId, content);
            sendPacket(ClientProtocol.U_WORLD_MESSAGE_SYNC, msg, userId);
        } else if (code.equals("/chzbsetchengzhu")) {
            crossChuHanData.setProcess2WinUserId(userId);
        } else if (code.equals("/chzbsetpvebuff")) {
            List<Integer> list = StringUtils.stringToIntegerList(param, "=");
            int cityType = list.get(0);
            int pveBuff = list.get(1);
            int userCamp = userData.getCampId();
            CrossChuHanCampData campData = campDataMap.get(userCamp);
            campData.setPveBuff(cityType, pveBuff);
        } else if (code.equals("/chzbcalcsoldier")) {
            long teamAbility = Long.valueOf(param);

            StringBuilder sb = new StringBuilder();
            // 【计算】
            BigDecimal paramAbility = BigDecimal.valueOf(teamAbility);
            BigDecimal param1 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(0));
            BigDecimal param2 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(1));
            BigDecimal param3 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(2));
            BigDecimal param4 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(3));
            BigDecimal param5 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(4));

            BigDecimal param6 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(5));
            BigDecimal param7 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(6));
            BigDecimal param8 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(7));
            BigDecimal param9 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(8));
            BigDecimal param10 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(9));
            BigDecimal param11 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(10));
            BigDecimal param12 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(11));
            sb.append("参数1=").append(param1).append("\r\n");
            sb.append("参数2=").append(param2).append("\r\n");
            sb.append("参数3=").append(param3).append("\r\n");
            sb.append("参数4=").append(param4).append("\r\n");
            sb.append("参数5=").append(param5).append("\r\n");
            sb.append("参数6=").append(param6).append("\r\n");
            sb.append("参数7=").append(param7).append("\r\n");
            sb.append("参数8=").append(param8).append("\r\n");
            sb.append("参数9=").append(param9).append("\r\n");
            sb.append("参数10=").append(param10).append("\r\n");
            sb.append("参数11=").append(param11).append("\r\n");
            sb.append("参数12=").append(param12).append("\r\n");

            // log x param2
            double logValue = Math.log(paramAbility.divide(param1, 8, BigDecimal.ROUND_DOWN).doubleValue());
            sb.append("log  队伍赚钱/参数1=").append(logValue).append("\r\n");
            BigDecimal logPart = BigDecimal.valueOf(logValue).multiply(param2.divide(param3, 8, BigDecimal.ROUND_DOWN)).setScale(8, BigDecimal.ROUND_DOWN);
            sb.append("log x (参数2/参数3)=").append(logPart).append("\r\n");

            // 左边一大串
            //取max
            BigDecimal leftPart = BigDecimal.valueOf(Math.max(logPart.doubleValue(), param4.doubleValue()));
            sb.append("参数4 取max=").append(leftPart).append("\r\n");

            // 取完max乘 参数5/参数6
            leftPart = leftPart.multiply(param5.divide(param6, 8, BigDecimal.ROUND_DOWN)).setScale(8, BigDecimal.ROUND_DOWN);
            sb.append("左边 取完max乘 (参数5/参数6)=").append(leftPart).append("\r\n");

            BigDecimal rightPart = BigDecimal.valueOf(Math.pow(paramAbility.doubleValue(), param7.divide(param8, 8, BigDecimal.ROUND_DOWN).doubleValue()));
            sb.append("右边 队伍赚钱的(参数7/参数8)的次方 =").append(rightPart).append("\r\n");
            rightPart = rightPart.multiply(param9.divide(param10, 8, BigDecimal.ROUND_DOWN)).setScale(8, BigDecimal.ROUND_DOWN);
            sb.append("右边 次方 x (参数9/参数10)=").append(rightPart).append("\r\n");
            //取max
            rightPart = BigDecimal.valueOf(Math.max(rightPart.doubleValue(), param4.doubleValue()));
            sb.append("参数4 取max=").append(rightPart).append("\r\n");
            rightPart = rightPart.multiply(param11.divide(param12, 8, BigDecimal.ROUND_DOWN)).setScale(8, BigDecimal.ROUND_DOWN);
            sb.append("右边 取max x (参数11/参数12)=").append(rightPart).append("\r\n");

            leftPart = leftPart.add(rightPart);
            sb.append("左边 + 右边=").append(leftPart).append("\r\n");

            String content = String.format("输入队伍战力%s,公式计算后得到结果%s", param, leftPart);
            content = sb.append(content).toString();


            WorldMessageProto.WorldMessageTempMsg.Builder msg = parseChatMsg(eMessageType.ChuHanPublicChat.getValue(), userId, content);

            sendPacket(ClientProtocol.U_WORLD_MESSAGE_SYNC, msg, userId);
        } else if (code.equals("/chzbgetbuff")) {
            //发送所有的BUFF
            StringBuilder campBuffHave = new StringBuilder();
            for (CrossChuHanCampData campData : campDataMap.values()) {
                campBuffHave.append("阵营").append(campData.getCampId()).append("拥有BUFF:");
                List<List<Integer>> effectCampBuffList = getEffectCampBuffList(campData.getCampId());
                for (List<Integer> buffInfos : effectCampBuffList) {
                    int buffId = buffInfos.get(0);
                    int buffLevel = buffInfos.get(1);
                    campBuffHave.append(buffId).append("=" + buffLevel + "级").append("=");
                    ChuHanBuffTypeConfig buffConfig = getConfig().getBuffTypeConfigMap().get(buffId);
                    eChuHanBuffType buffType = eChuHanBuffType.forValue(buffConfig.getType());
                    List<Integer> buffValues = buffConfig.getBuffParamsByLevel(buffLevel);
                    campBuffHave.append("加成值=").append(StringUtils.listToString(buffValues, ",")).append(";");
                    campBuffHave.append("加成说明:").append(buffConfig.getDesc()).append("\r\n");
                }
            }

            String content = campBuffHave.toString();
            WorldMessageProto.WorldMessageTempMsg.Builder msg = parseChatMsg(eMessageType.ChuHanPublicChat.getValue(), userId, content);
            sendPacket(ClientProtocol.U_WORLD_MESSAGE_SYNC, msg, userId);
        } else if (code.equals("/chzballbattle")) {//把所有有兵的队伍拉出来打架,随机一个战令城市打架
            int targetCityId = Integer.parseInt(param);
            int day = ActivityHelper.getWhichDayAfterBeginTime(getConfig().getActivityInfo());
            CrossChuHanOrderCity orderCity = crossChuHanData.getOrderCityMap().get(day);
            List<Integer> cityIds = new ArrayList<>();
            for (List<Integer> tempList : orderCity.getOrderCityMap().values()) {
                cityIds.addAll(tempList);
            }
            int targetCityCamp = 0;
            if (targetCityId > 0){
                ChuHanCityConfig toCityConfig = config.getCityConfigMap().get(targetCityId);
                if(config.isPveCityByCityType(toCityConfig.getType())){
                    //PVE只能去自己阵营的PVE城池
                    targetCityCamp = toCityConfig.getMapCamp();
                }
            }

            for (CrossChuHanUserData chuHanUserData : userDataMap.values()) {
                if (chuHanUserData.getPatronsMap().isEmpty()) {
                    continue;
                }

                if (targetCityCamp > 0) {
                    if (chuHanUserData.getCampId() != targetCityCamp) {
                        continue;
                    }
                }

                //先给兵
                CrossChuHanBarracks barracks = chuHanUserData.getBarracks();
                if (barracks.getReadyNum() < 1000000) {//给所有人补到100W以上的兵
                    barracks.setReadyNum(barracks.getReadyNum() + 1000000);
                }

                //找个军令城市随机打
                for (CrossChuHanTeam team : chuHanUserData.getTeamMap().values()) {
                    Integer cityId = RandomHelper.getRandomList(cityIds, 1).get(0);
                    if (targetCityId != 0) {
                        cityId = targetCityId;
                    }
                    if (team.getTargetCityId() != cityId) {
                        team.setTargetCityId(cityId);
                    }

                }
            }
            String content = "每人100W兵开始打";
            WorldMessageProto.WorldMessageTempMsg.Builder msg = parseChatMsg(eMessageType.ChuHanPublicChat.getValue(), userId, content);
            sendPacket(ClientProtocol.U_WORLD_MESSAGE_SYNC, msg, userId);
        }else if (code.equals("/chzbtest")) {
            String content = "玩家带兵上限BUFF加";
            int buffAddition = getBuffAddition(userData.getUserId(), eChuHanBuffType.Type1_TeamSoldierMaxNum).get(0);
            content += buffAddition + "\r\n";
            for (CrossChuHanTeam team : userData.getTeamMap().values()) {
                long teamMaxSoldier = getTeamMaxSoldier(userData, team);
                content += "队伍id" + team.getOccupation() + "=" + teamMaxSoldier;
            }
//            for (CrossChuHanUserData hanUserData : userDataMap.values()) {
//                for (CrossChuHanTeam team : hanUserData.getTeamMap().values()) {
//                    if (team.getTargetCityId() > 0) {
//                        team.setTargetCityId(0);
//                        addNeedSyncTeam(team);
//                    }
//                }
//                hanUserData.setUpdateOption();
//            }
            WorldMessageProto.WorldMessageTempMsg.Builder msg = parseChatMsg(eMessageType.ChuHanPublicChat.getValue(), userId, content);
            sendPacket(ClientProtocol.U_WORLD_MESSAGE_SYNC, msg, userId);
        }

        return "";
    }

    private WorldMessageProto.WorldMessageTempMsg.Builder parseChatMsg( int messageType, long userId, String content) {
        return WorldMessageMgr.parseWorldMessageTempMsg(CrossUserMgr.getCrossUserBaseInfo(userId), messageType, eChatContentType.Common.getContentType(), content, System.currentTimeMillis(), getConfig().getActivityInfo().getActivityId(), "");
    }


}